diff --git a/.gitignore b/.gitignore index 0c5a1bd..ed3ff44 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ app.*.map.json /android/app/build/ /android/build/ keystore_base64.txt - +graphify-out* +*.txt* \ No newline at end of file diff --git a/README_ES.md b/README_ES.md new file mode 100644 index 0000000..a909f8c --- /dev/null +++ b/README_ES.md @@ -0,0 +1,20 @@ +# NFile - Traduccion al Espanol + +Traduccion completa de la aplicacion NFile al espanol. + +## Cambios realizados + +- **Todos los textos de la interfaz** traducidos al espanol (~1000 strings en 55+ archivos Dart) +- **Notificaciones de Android** traducidas (FTP, Web Sharing, Audio Player, Archive Operations) +- **Sistema de localizacion** implementado en `lib/core/app_strings.dart` con soporte para ingles y espanol +- **APK compilado** listo para instalar + +## Creditos + +- Traduccion por: Skuuill +- Contacto: moreappmic@gmail.com +- App original: [NFile](https://github.com/MSOB7YY/NFile) + +## Licencia + +GNU GPL v3 (igual que el proyecto original) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index df186c1..fdde7e7 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -47,7 +47,7 @@ android { versionCode = flutter.versionCode versionName = flutter.versionName androidResources { - localeFilters.addAll(listOf("en")) + localeFilters.addAll(listOf("en", "es")) } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 65a8ecc..95c3d3f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -96,6 +96,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = Build.VERSION_CODES.O) { - val name = "FTP Server" - val descriptionText = "Displays status of the background FTP Server" + val name = getString(R.string.ftp_server_channel_name) + val descriptionText = getString(R.string.ftp_server_channel_desc) val importance = NotificationManager.IMPORTANCE_LOW val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText diff --git a/android/app/src/main/kotlin/com/rubex/nfile/MainActivity.kt b/android/app/src/main/kotlin/com/rubex/nfile/MainActivity.kt index c67c7b8..029d0c1 100644 --- a/android/app/src/main/kotlin/com/rubex/nfile/MainActivity.kt +++ b/android/app/src/main/kotlin/com/rubex/nfile/MainActivity.kt @@ -44,6 +44,7 @@ class MainActivity : AudioServiceFragmentActivity() { private var pendingPermissionResult: MethodChannel.Result? = null private var safPermissionResult: MethodChannel.Result? = null private val SAF_REQUEST_CODE = 10002 + private var isPickerMode = false private val ACTION_CANCEL_OPERATION = "com.rubex.nfile.ACTION_CANCEL_OPERATION" private var notificationsChannel: MethodChannel? = null @@ -66,6 +67,7 @@ class MainActivity : AudioServiceFragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + isPickerMode = intent?.action == Intent.ACTION_GET_CONTENT || intent?.action == Intent.ACTION_PICK try { Shizuku.addBinderReceivedListenerSticky { // Binder ready @@ -140,6 +142,39 @@ class MainActivity : AudioServiceFragmentActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) + + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.rubex.nfile/picker").setMethodCallHandler { call, result -> + when (call.method) { + "isPickerMode" -> result.success(isPickerMode) + "finishWithResult" -> { + val filePath = call.argument("path") + if (filePath != null) { + try { + val file = File(filePath) + val uri = androidx.core.content.FileProvider.getUriForFile( + this@MainActivity, + "${applicationContext.packageName}.fileprovider", + file + ) + val resultIntent = Intent().apply { + data = uri + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } + setResult(android.app.Activity.RESULT_OK, resultIntent) + finish() + result.success(true) + } catch (e: Exception) { + e.printStackTrace() + result.success(false) + } + } else { + result.success(false) + } + } + else -> result.notImplemented() + } + } + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "checkStatus" -> { @@ -710,11 +745,11 @@ class MainActivity : AudioServiceFragmentActivity() { notificationsChannel?.setMethodCallHandler { call, result -> val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "nfile_archive_channel" - val channelName = "NFile Archive Operations" + val channelName = getString(R.string.archive_channel_name) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW).apply { - description = "Shows progress of file compression and extraction" + description = getString(R.string.archive_channel_desc) } notificationManager.createNotificationChannel(channel) } @@ -762,8 +797,8 @@ class MainActivity : AudioServiceFragmentActivity() { .setContentIntent(openPendingIntent) if (progress < max) { - builder.addAction(android.R.drawable.ic_menu_view, "Open", openPendingIntent) - builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Cancel", cancelPendingIntent) + builder.addAction(android.R.drawable.ic_menu_view, "Abrir", openPendingIntent) + builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Cancelar", cancelPendingIntent) } if (indeterminate) { diff --git a/android/app/src/main/kotlin/com/rubex/nfile/NFileDocumentsProvider.kt b/android/app/src/main/kotlin/com/rubex/nfile/NFileDocumentsProvider.kt index 8d7d5a7..61bb6fd 100644 --- a/android/app/src/main/kotlin/com/rubex/nfile/NFileDocumentsProvider.kt +++ b/android/app/src/main/kotlin/com/rubex/nfile/NFileDocumentsProvider.kt @@ -47,9 +47,9 @@ class NFileDocumentsProvider : DocumentsProvider() { row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocIdForFile(File("/storage/emulated/0"))) row.add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*") row.add(DocumentsContract.Root.COLUMN_FLAGS, flags) - row.add(DocumentsContract.Root.COLUMN_TITLE, "NFile Storage") - row.add(DocumentsContract.Root.COLUMN_SUMMARY, "Internal storage via NFile") - row.add(DocumentsContract.Root.COLUMN_ICON, android.R.drawable.sym_def_app_icon) + row.add(DocumentsContract.Root.COLUMN_TITLE, context?.getString(R.string.storage_provider_title) ?: "NFile Storage") + row.add(DocumentsContract.Root.COLUMN_SUMMARY, context?.getString(R.string.storage_provider_desc) ?: "Internal storage via NFile") + row.add(DocumentsContract.Root.COLUMN_ICON, R.mipmap.ic_launcher) try { val stat = android.os.StatFs("/storage/emulated/0") @@ -112,6 +112,15 @@ class NFileDocumentsProvider : DocumentsProvider() { return getDocIdForFile(file) } + override fun renameDocument(documentId: String?, displayName: String?): String { + val file = getFileForDocId(documentId ?: "") + val newFile = File(file.parentFile, displayName ?: file.name) + if (!file.renameTo(newFile)) { + throw FileNotFoundException("Failed to rename document: $documentId") + } + return getDocIdForFile(newFile) + } + override fun deleteDocument(documentId: String?) { val file = getFileForDocId(documentId ?: "") if (!file.deleteRecursively()) { @@ -153,7 +162,8 @@ class NFileDocumentsProvider : DocumentsProvider() { private fun includeFile(result: MatrixCursor, docId: String?, file: File) { val flags = DocumentsContract.Document.FLAG_SUPPORTS_DELETE or - DocumentsContract.Document.FLAG_SUPPORTS_WRITE + DocumentsContract.Document.FLAG_SUPPORTS_WRITE or + DocumentsContract.Document.FLAG_SUPPORTS_RENAME val mimeType = getMimeType(file) val finalFlags = if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) { diff --git a/android/app/src/main/kotlin/com/rubex/nfile/WebSharingForegroundService.kt b/android/app/src/main/kotlin/com/rubex/nfile/WebSharingForegroundService.kt index 1cf8b03..94f4077 100644 --- a/android/app/src/main/kotlin/com/rubex/nfile/WebSharingForegroundService.kt +++ b/android/app/src/main/kotlin/com/rubex/nfile/WebSharingForegroundService.kt @@ -48,11 +48,11 @@ class WebSharingForegroundService : Service() { Notification.Builder(this) } - val title = if (isInternet) "NFile Internet Web Share" else "NFile Local Web Share" + val title = if (isInternet) getString(R.string.web_share_internet) else getString(R.string.web_share_local) val notification = builder .setContentTitle(title) - .setContentText("Running at $url") + .setContentText(getString(R.string.web_share_running, url)) .setSmallIcon(iconResId) .setContentIntent(pendingIntent) .setOngoing(true) @@ -69,8 +69,8 @@ class WebSharingForegroundService : Service() { private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val name = "Web Sharing Server" - val descriptionText = "Displays status of the background Web Sharing Server" + val name = getString(R.string.web_share_channel_name) + val descriptionText = getString(R.string.web_share_channel_desc) val importance = NotificationManager.IMPORTANCE_LOW val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText diff --git a/android/app/src/main/res/values-es/strings.xml b/android/app/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..19e4a67 --- /dev/null +++ b/android/app/src/main/res/values-es/strings.xml @@ -0,0 +1,19 @@ + + + Servidor FTP NFile + Ejecutándose en ftp://%1$s:%2$d + Servidor FTP + Muestra el estado del Servidor FTP en segundo plano + + Operaciones de Archivo NFile + Muestra el progreso de compresión y extracción de archivos + + Almacenamiento NFile + Almacenamiento interno vía NFile + + NFile Compartición Web por Internet + NFile Compartición Web Local + Ejecutándose en %1$s + Servidor de Compartición Web + Muestra el estado del Servidor de Compartición Web en segundo plano + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..de3f5b3 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,19 @@ + + + NFile FTP Server + Running at ftp://%1$s:%2$d + FTP Server + Displays status of the background FTP Server + + NFile Archive Operations + Shows progress of file compression and extraction + + NFile Storage + Internal storage via NFile + + NFile Internet Web Share + NFile Local Web Share + Running at %1$s + Web Sharing Server + Displays status of the background Web Sharing Server + diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..1ae4720 --- /dev/null +++ b/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e4ef43f..e496849 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip diff --git a/assets/i18n/en.json b/assets/i18n/en.json new file mode 100644 index 0000000..1b44645 --- /dev/null +++ b/assets/i18n/en.json @@ -0,0 +1,868 @@ +{ + "cancel": "Cancel", + "ok": "OK", + "save": "Save", + "delete": "Delete", + "rename": "Rename", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "share": "Share", + "extract": "Extract", + "archive": "Archive", + "close": "Close", + "done": "Done", + "back": "Back", + "exit": "Exit", + "home": "Home", + "browse": "Browse", + "search": "Search", + "selectAll": "Select All", + "refresh": "Refresh", + "properties": "Properties", + "info": "Info", + "more": "More", + "preview": "Preview", + "create": "Create", + "open": "Open", + "edit": "Edit", + "upload": "Upload", + "download": "Download", + "connect": "Connect", + "disconnect": "Disconnect", + "restore": "Restore", + "clear": "Clear", + "skip": "Skip", + "replace": "Replace", + "keepBoth": "Keep Both", + "uninstall": "Uninstall", + "backup": "Backup", + "confirm": "Confirm", + "appTitle": "NFile", + "appSubtitle": "Beautiful Media Suite", + "grantPermission": "Grant Permission", + "storageAccessRequired": "Storage Access Required", + "storagePermissionMessage": "NFile requires storage permission to manage, organize, and display your media files seamlessly.", + "openingSharedDocument": "Opening shared document...", + "resolvingSecureContent": "Resolving secure content stream", + "myFiles": "My Files", + "refreshDashboard": "Refresh Dashboard", + "dashboardRefreshed": "Dashboard refreshed successfully", + "exitApplication": "Exit Application", + "exitConfirmation": "Exit Confirmation", + "exitConfirmationMessage": "Are you sure you want to exit? Press back again or tap Exit to close the app.", + "pressBackAgain": "Press back again to exit", + "confirmDeletion": "Confirm Deletion", + "deletePermanently": "Delete Permanently", + "deletePermanentlyQuestion": "Delete Permanently?", + "deleteSelectedItems": "Delete Selected Items", + "deleteSourceFiles": "Delete source files after completion", + "permanentlyDeleteItems": "Are you sure you want to permanently delete {count} selected items?", + "permanentlyDeleteItemsArchive": "Are you sure you want to delete precisely these {count} item(s) from the archive? This cannot be undone.", + "deletedSuccessfully": "Successfully deleted {count}", + "permanentlyDeleted": "Permanently deleted {count} item(s)", + "failedToDelete": "Failed to delete items", + "errorDeleting": "{e}'", + "itemsDeleted": "Items deleted successfully", + "recycleBin": "Recycle Bin", + "emptyRecycleBin": "Empty Recycle Bin", + "emptyRecycleBinQuestion": "Empty Recycle Bin?", + "emptyBin": "Empty Bin", + "recycleBinEmptied": "Recycle Bin emptied successfully", + "errorEmptyingBin": "{e}'", + "emptyRecycleBinMessage": "Are you sure you want to permanently delete all items in the Recycle Bin? This action is irreversible.", + "deletePermanentlyRecycleMessage": "Are you sure you want to permanently delete these {count} item(s)? This action cannot be undone.", + "select": "{n} Selected", + "searchDeletedFiles": "Search deleted files...", + "restoredItems": "Restored {count} item(s) successfully", + "errorRestoring": "{e}'", + "moreSettings": "More Settings", + "searchSettings": "Search settings...", + "generalAndBehavior": "General & Behavior", + "generalAndBehaviorSub": "Default screen, navigation controls, and shortcuts", + "appearanceAndThemes": "Appearance & Themes", + "appearanceAndThemesSub": "Themes, app icons, folder styles, and typography", + "fileExplorerOptions": "File Explorer Options", + "fileExplorerOptionsSub": "Address bar, hidden files, tabs, and drag & drop", + "listAndLayout": "List & Layout Styling", + "listAndLayoutSub": "Folder sizes, counts, and time/date formats", + "mediaPreferences": "Media Preferences", + "mediaPreferencesSub": "Default album view and thumbnail previews", + "fileActionsAndViewers": "File Actions & Viewers", + "fileActionsAndViewersSub": "Open actions and default viewers configuration", + "recycleBinTrash": "Recycle Bin (Trash)", + "recycleBinTrashSub": "Recycle bin toggles and auto-delete duration", + "backupAndRestore": "Backup & Restore", + "backupAndRestoreSub": "Backup your settings to a JSON file or restore them", + "defaultToBrowseScreen": "Default to Browse Screen", + "defaultToBrowseScreenSub": "Directly launch into the Browse storage explorer on app start", + "rememberLastFolder": "Remember Last Opened Folder", + "rememberLastFolderSub": "Open the last folder you browsed when launching the app", + "showHomeBrowseBar": "Show Home & Browse Bottom Bar", + "showHomeBrowseBarSub": "Toggle bottom navigation bar visibility on the Home screen", + "hideNavLabels": "Hide Bottom Navigation Labels", + "hideNavLabelsSub": "Hide text labels of the bottom bar (Home/Browse) for a cleaner and compact look", + "hideAndroidNavBar": "Hide Android Navigation Bar", + "hideAndroidNavBarSub": "Hide bottom navigation bar to maximize screen real estate (swiping up displays it)", + "showBottomNavBar": "Show Bottom Navigation Bar", + "showBottomNavBarSub": "Enable bottom action bar on Browse screen", + "hideActionBarLabels": "Hide Action Bar Text Labels", + "hideActionBarLabelsSub": "Show only icons in selection action bar at bottom of Browse & Media screens", + "customizeShortcuts": "Customize Shortcuts", + "customizeShortcutsSub": "Reorder and toggle visibility of quick category items", + "showRecentFiles": "Show Recent Files", + "showRecentFilesSub": "Display the list of recently accessed files on the Home screen", + "preventLeftBackGesture": "Prevent Left Back Gesture for Drawer", + "preventLeftBackGestureSub": "Excludes the left edge of the screen from Android system back gestures, making it easier to swipe open the drawer. You can still swipe from the right edge to go back.", + "appExitBehavior": "App Exit Behavior", + "accentColorTheme": "Accent Color / Dynamic Theme", + "folderIconStyle": "Folder Icon Style", + "appDrawerButtonStyle": "App Drawer Button Style", + "amoledBlackMode": "AMOLED Black Mode", + "amoledBlackModeSub": "Use pitch black background in Dark Mode for AMOLED screens", + "appIcon": "App Icon", + "appTypography": "App Typography / Font Family", + "useMaterialIcons": "Use Expressive Material Icons", + "useMaterialIconsSub": "Replace custom Broken icons with standard Material Design icons", + "showAddressBar": "Show Address Bar", + "showAddressBarSub": "Display an editable Windows-Explorer-style address bar at the top of file list", + "showFloatingButton": "Show Floating '+' Button", + "showFloatingButtonSub": "Enable quick creation (+) button at bottom of Browse screen", + "showHiddenFiles": "Show Hidden Files", + "showHiddenFilesSub": "Display system files and folders starting with a dot (.)", + "highlightExitedFolder": "Highlight Exited Folder", + "highlightExitedFolderSub": "Briefly flash and scroll to the folder you just exited when going back", + "enableMultipleTabs": "Enable Multiple Tabs", + "enableMultipleTabsSub": "Allow opening multiple folders in separate tabs for quick navigation", + "enableSplitScreen": "Enable Split Screen", + "enableSplitScreenSub": "Browse two directories side by side and transfer files easily", + "enableDragAndDrop": "Enable Drag & Drop", + "enableDragAndDropSub": "Long press and drag folders or files to move them into other folders", + "confirmDragDrop": "Confirm Drag & Drop Actions", + "confirmDragDropSub": "Show options popup (Copy, Move, Archive) when dropping files", + "showFolderFileCount": "Show Folder & File Count Header", + "showFolderFileCountSub": "Display total folders and files count under storage title bar", + "showFolderContentCount": "Show Folder Content Count", + "showFolderContentCountSub": "Calculate and display total files and folders inside directory listings", + "showFolderSize": "Show Folder Size", + "showFolderSizeSub": "Calculate and display total size of all files inside directories (can affect listing performance)", + "use24HourFormat": "Use 24-Hour Time Format", + "use24HourFormatSub": "Toggle between 12-hour (AM/PM) and 24-hour time formatting across lists", + "hideTimeDate": "Hide Time & Date from Lists", + "hideTimeDateSub": "Completely hide modification dates and times under files and folders", + "adaptiveMultiLine": "Adaptive Multi-line Filenames", + "adaptiveMultiLineSub": "Allow filenames to wrap 3 lines instead of truncating", + "hide3DotButtons": "Hide 3-Dot Action Buttons", + "hide3DotButtonsSub": "Hide the three-dot option menu button next to folders and files", + "threeDotDisabledInfo": "3-Dot Disabled Trailing Info", + "defaultAlbumView": "Default Album Preferred View", + "defaultAlbumViewSub": "Open Images/Videos quick categories directly in Folders (Albums) preferred view", + "showMediaPreviews": "Show Media Previews", + "showMediaPreviewsSub": "Display actual image and video thumbnails instead of generic file icons", + "skipOpenWithDialog": "Skip \"Open With\" Dialog", + "skipOpenWithDialogSub": "Bypass the application choice dialog and immediately open files with default viewers", + "resetDefaultViewers": "Reset Default File Viewers", + "resetDefaultViewersSub": "Clear all remembered \"Open With\" associations for file viewers", + "viewerChoicesReset": "All default viewer choices have been reset", + "enableRecycleBin": "Enable Recycle Bin", + "enableRecycleBinSub": "Move deleted files and folders to a hidden Recycle Bin instead of deleting permanently", + "autoDeleteTrashDuration": "Auto-Delete Trash Duration", + "backupSettings": "Backup Settings", + "backupSettingsSub": "Save all your current settings to NFile/Backups/Settings/", + "restoreSettings": "Restore Settings", + "restoreSettingsSub": "Select and restore settings from a JSON backup file", + "settingsBackedUp": "Settings backed up to NFile/Backups/Settings/nfile_settings_backup.json", + "settingsRestored": "Settings restored successfully!", + "failedToBackup": "{e}'", + "failedToRestore": "{e}'", + "chooseTrailingInfoStyle": "Choose Trailing Info Style", + "chooseExitBehavior": "Choose Exit Behavior", + "chooseAccentTheme": "Choose Accent Theme", + "chooseFolderIconStyle": "Choose Folder Icon Style", + "chooseDrawerButtonStyle": "Choose Drawer Button Style", + "appIconPicker": "App Icon Picker", + "appLauncherIcon": "App Launcher Icon", + "logo": "Logo", + "logo1": "Logo 1", + "logo2": "Logo 2", + "logo3": "Logo 3", + "logo4": "Logo 4", + "appIconSwitched": "App icon switched to {title} successfully!", + "customFontLoaded": "Custom font loaded", + "failedToLoadFont": "Failed to load the selected font file.", + "invalidFileType": "Invalid File Type", + "invalidFileTypeMessage": "Please select a valid OpenType (.otf) or TrueType (.ttf) font file.", + "removeCustomFont": "Remove Custom Font", + "customFontRemoved": "Custom font removed.", + "pleaseSelectValidBackup": "Please select a valid .json settings backup file", + "systemAppDisabled": "System App Disabled", + "failedToRequestSaf": "{e}'", + "enterConnectionName": "Please enter a connection name", + "enterServerAddress": "Please enter server address / hostname", + "connectionFailed": "{e}'", + "http": "HTTP", + "httpsSecure": "HTTPS (Secure)", + "retryConnection": "Retry Connection", + "uploadClipboardHere": "Upload Clipboard Here", + "uploadLocalClipboard": "Upload local clipboard to server", + "newFolder": "New Folder", + "folderName": "Folder name", + "copyToLocalDevice": "Copy to Local Device", + "moveToLocalDevice": "Move to Local Device", + "deleteQuestion": "Delete", + "navigation": "Navigation", + "systemRoot": "System Root", + "globalSearch": "Global Search", + "serversAndTools": "Servers & Tools", + "privateWallet": "Private Wallet", + "ftpServer": "FTP Server", + "webSharing": "Web Sharing", + "addRemoteConnection": "Add Remote Connection", + "quickCategories": "Quick Categories", + "addShortcut": "Add Shortcut", + "customizationAndSettings": "Customization & Settings", + "lightMode": "Light Mode", + "darkMode": "Dark Mode", + "aboutNFile": "About NFile", + "couldNotOpenLink": "{url}'", + "starOnRepository": "Star on Repository", + "joinTelegram": "Join Telegram Channel", + "shareAppWithFriends": "Share App with Friends", + "exploreGitHubSource": "Explore GitHub Source Code", + "copedToClipboard": "Copied to clipboard", + "cutToClipboard": "Cut to clipboard", + "copedNItems": "Copied {n} item(s)", + "cutNItems": "Cut {n} item(s)", + "copedToClipboardN": "Copied {n} items to clipboard", + "copedLabelToClipboard": "Copied {label} to clipboard", + "cutToClipboardN": "Cut {n} items to clipboard", + "copedSelected": "Copied selected items", + "cutSelected": "Cut selected items", + "pastedSuccessfully": "Pasted successfully", + "pastedNItems": "Pasted {n} item(s)", + "pastedItemsTo": "Pasted {count} items to {dest}", + "noShareableItems": "No shareable items found.", + "noFilesToShare": "No files available to share", + "errorSharing": "{e}'", + "errorPreparingFiles": "{e}'", + "errorReadingSharedFile": "{e}'", + "fileNotFoundOrNotShareable": "File not found or not shareable.", + "cannotMoveIntoItself": "Cannot move a folder inside itself or same location", + "cannotCopyIntoItself": "Cannot copy a folder inside itself or same location", + "movedSuccessfully": "Moved {name} successfully", + "copedSuccessfully": "Copied {name} successfully", + "failedToMove": "{e}'", + "failedToCopy": "{e}'", + "failedToTransfer": "{e}'", + "failedToConnectRemote": "{e}'", + "pasteHere": "Paste Here", + "pasteHereN": "Paste Here ({n})", + "actionCancelled": "Action cancelled / Clipboard cleared", + "extractToCurrentFolder": "Extract to Current Folder", + "addFile": "Add File", + "addCustomPath": "Add Custom Path", + "customPaths": "{n} custom path(s)", + "addFolderFileShortcut": "Add Folder / File Shortcut", + "customPathsTooltip": "Custom Paths", + "deleteShortcut": "Delete Shortcut", + "restoreLocation": "Restore Location", + "excludeLocation": "Exclude Location", + "addNetworkConnection": "Add Network Connection", + "removeConnection": "Remove Connection", + "selectMode": "Select Mode", + "viewAndSortOptions": "View & Sort Options", + "storageVolumes": "Storage Volumes & SD Card", + "createNew": "Create New", + "openWith": "Open with...", + "justOnce": "Just once", + "always": "Always", + "openWithApp": "Open with App", + "shareComingSoon": "Share coming soon", + "savedSuccessfully": "Saved successfully", + "errorSaving": "{e}'", + "errorLoading": "{e}'", + "standardMode": "Standard Mode", + "lagFreeMode": "Lag-Free Mode", + "continuous": "Continuous", + "singlePage": "Single Page", + "vertical": "Vertical", + "horizontal": "Horizontal", + "enableTextSelection": "Enable Text Selection", + "displaySettings": "Display Settings", + "emptySheet": "Empty Sheet", + "htmlPreview": "HTML Preview", + "markdownPreview": "Markdown Preview", + "reload": "Reload", + "selectSyntax": "Select Syntax", + "findReplace": "Find / Replace", + "saveFile": "Save File", + "moreOptions": "More Options", + "defaultZoom": "Default Zoom ({pt})", + "syntax": "Syntax ({lang})", + "find": "Find...", + "replaceWith": "Replace with...", + "replaceAll": "Replace All", + "undo": "Undo", + "redo": "Redo", + "fileSaved": "File saved successfully", + "errorLoadingFile": "{e}'", + "errorSavingFile": "{e}'", + "replacedOccurrences": "Replaced {n} occurrences", + "ftpServerStarted": "FTP Server started at ftp://{ip}:{port}", + "ftpServerStopped": "FTP Server stopped successfully", + "errorStartingFtp": "{e}'", + "stopServerBeforeConfig": "Please stop the server before changing configuration", + "changePort": "Change Port", + "portNumber": "Port Number", + "portHint": "e.g., 9999", + "invalidPort": "Invalid port number", + "setUsername": "Set Username", + "username": "Username", + "usernameCannotBeEmpty": "Username cannot be empty", + "stopServerBeforeEditing": "Stop the server before editing settings", + "ftpShortcutAdded": "FTP Server shortcut added to home screen!", + "changeDirectory": "Change directory", + "changePortOption": "Change port", + "setUser": "Set user", + "anonymousAccess": "Anonymous access", + "createShortcut": "Create shortcut", + "homeDirectory": "Home directory", + "userName": "User name", + "showHiddenFilesFtp": "Show hidden files", + "ftpes": "FTPES", + "ftpesDescription": "Secure FTP connection over explicit TLS", + "webSharingStarted": "{url}'", + "webSharingStopped": "Local HTTP Sharing Server stopped.", + "errorStartingWeb": "{e}'", + "internetCloudTunnel": "Internet cloud tunnel online! Temporary link active.", + "failedToStartCloud": "{e}'", + "linkCopied": "Link copied to clipboard!", + "internetShareDeactivated": "Internet Share Tunnel deactivated.", + "copyUrl": "Copy URL", + "qrCode": "QR Code", + "copyLink": "Copy Link", + "noRecentFiles": "No recent files", + "successfullyDeleted": "Successfully deleted items", + "folderIsEmpty": "Folder is empty", + "couldNotReadArchive": "Could not read archive", + "extractedItem": "Extracted {name} to {dest}", + "copiedPhysicalItems": "{n} item(s) copied to clipboard", + "addedSuccessfully": "Successfully added {n} item(s) into archive", + "pastedCountItems": "Pasted {n} item(s) into archive", + "createArchive": "Create Archive", + "archiveName": "Archive Name", + "archiveFormat": "Archive Format", + "passwordOptional": "Password (Optional)", + "splitVolumeSize": "Split Volume Size in MB (Optional)", + "leaveEmptyForSingle": "Leave empty for single archive", + "createSeparateArchive": "Create separate archive for each file", + "createArchiveFailed": "{e}'", + "extractToFolder": "Extract to Folder", + "passwordIfEncrypted": "Password (if encrypted)", + "cancelPaste": "Cancel Paste", + "renameFile": "Rename File", + "newFilename": "New filename", + "namePattern": "Name Pattern", + "extensionLabel": "Extension", + "padding": "Padding", + "startNumber": "Start Number", + "findText": "Find text", + "searchTerm": "Search term", + "replacement": "Replacement", + "originalName": "Original name (%)", + "sequentialNumber": "Sequential number (#)", + "tripleSequentialNumber": "Triple sequential number (###)", + "fileNameWithoutExtension": "File name without extension ({n})", + "extensionWithDot": "Extension with dot ({de})", + "extensionWithoutDot": "Extension without dot ({e})", + "fullNameWithExtension": "Full name with extension ({N})", + "background": "Background", + "cancelOperation": "Cancel Operation", + "transferSpeed": "Transfer Speed", + "estTime": "Est. Time", + "dataProcessed": "Data Processed", + "showInLocation": "Show in location", + "copySelected": "Copy Selected", + "cutSelectedB": "Cut Selected", + "archiveCompress": "Archive (Compress)", + "propertiesAndInfo": "Properties & Info", + "deleteSelected": "Delete Selected", + "enterAbsolutePath": "Enter absolute path...", + "pathNotFound": "{path}'", + "copedPath": "{path}'", + "goToParentDirectory": "Go to Parent Directory", + "searchEllipsis": "Search...", + "useRootAccess": "Use Root Access (Superuser)", + "grantShizukuAccess": "Grant Shizuku Access (No Root)", + "howToSetupShizuku": "How to setup Shizuku?", + "newTab": "New Tab", + "duplicateTab": "Duplicate Tab", + "closeOtherTabs": "Close Other Tabs", + "closeTab": "Close Tab", + "allFiles": "All Files", + "documentsOnly": "Documents only", + "imagesOnly": "Images only", + "audioOnly": "Audio only", + "videosOnly": "Videos only", + "archivesOnly": "Archives only", + "extractingBundle": "Extracting package bundle for installation...", + "noInstallableApk": "No installable APK found in package bundle", + "failedToExtractBundle": "{e}'", + "failedToTriggerInstaller": "Failed to trigger split APK installer", + "sortBySize": "Sort by Size", + "sortAlphabetically": "Sort Alphabetically", + "rescanStorage": "Rescan Storage", + "selectAll_": "Select All", + "refreshList": "Refresh List", + "uninstallAppsTitle": "Uninstall Apps", + "confirmUninstallApps": "Are you sure you want to uninstall {n} selected app(s)?", + "backingUpApps": "Backing up selected applications...", + "backedUpApps": "Successfully backed up {n} app(s) to NFile/Backups/Apps/", + "failedToBackupApps": "{e}'", + "launchApplication": "Launch Application", + "systemSettingsDetails": "System Settings / Details", + "backUpApk": "Back Up APK", + "backingUpApk": "Backing up APK...", + "shareApkFile": "Share APK File", + "uninstallApplication": "Uninstall Application", + "restoreInstallApp": "Restore / Install App", + "shareBackupFile": "Share Backup File", + "deleteBackupFile": "Delete Backup File", + "newestFirst": "Newest First", + "oldestFirst": "Oldest First", + "dateWise": "Date Wise", + "newestFirstGrouped": "Newest First (Grouped per month)", + "oldestFirstGrouped": "Oldest First (Grouped per month)", + "sizeLargeFirst": "Size (Large First)", + "sizeSmallFirst": "Size (Small First)", + "lockOption": "Lock Option", + "secureImport": "Secure Import (Sandbox)", + "inPlaceScramble": "In-Place Scramble (Fast)", + "scramblingAndProtecting": "Scrambling & Protecting...", + "restored": "Restored", + "failedToRestoreFile": "{e}'", + "fileDeletedPermanently": "File deleted permanently.", + "failedToDeleteFile": "{e}'", + "decryptingSecurely": "Decrypting securely...", + "failedToDecrypt": "{e}'", + "securityDetails": "Security Details", + "errorLoadingVault": "{e}'", + "restoreUnhide": "Restore (Unhide)", + "details": "Details", + "searchScrambledFiles": "Search scrambled files...", + "permanentlyDeleteQuestion": "Are you sure you want to permanently delete ", + "clearAll": "Clear All", + "backspace": "Backspace", + "playbackSpeed": "Playback Speed", + "lockControls": "Lock Controls", + "repeatMode": "Repeat Mode", + "copyUrlTooltip": "Copy URL", + "mediaPathCopied": "Media path copied to clipboard.", + "volume": "Volume", + "brightness": "Brightness", + "sortOptions": "Sort Options", + "soundFX": "Sound FX", + "lyrics": "Lyrics", + "sleepTimer": "Sleep Timer", + "playingQueue": "Playing Queue ({count})", + "sleepTimerSet": "Sleep timer set for {mins} minutes.", + "mins": "{m} Minutes", + "soundAndSpeedFX": "Sound & Speed FX", + "pitchAdjustment": "Pitch Adjustment", + "resetToDefault": "Reset to Default", + "backgroundPlaybackStopped": "Background playback stopped", + "backgroundPlaybackEnabled": "Background playback enabled", + "viewSynchronizedLyrics": "View Synchronized Lyrics", + "soundFXAndEqualizer": "Sound FX & Equalizer", + "setSleepTimer": "Set Sleep Timer", + "audioFileInfo": "Audio File Info", + "lyricsLoaded": "Lyrics loaded successfully", + "loadLrcFile": "Load LRC File", + "noDataToExport": "No data to export.", + "exportedTo": "Successfully exported to {path}", + "exportFailed": "{e}'", + "noTablesFound": "No tables found in this database.", + "exportTableToCsv": "Export Table to CSV", + "searchRows": "Search rows...", + "noRowsFound": "No rows found", + "noSchemaLoaded": "No schema details loaded.", + "sqlEditor": "SQL Editor", + "selectTemplate": "SELECT template", + "enterSelectQuery": "Enter SELECT query here...", + "exportResultsToCsv": "Export Results to CSV", + "runQuery": "Run Query", + "typeLabel": "{type}'", + "defaultLabel": "{val}'", + "errorCreatingFolder": "{e}'", + "createFolder": "Create Folder", + "selectStorage": "Select Storage", + "clearSelection": "Clear Selection", + "pinSelected": "Pin Selected ({n})", + "pinThisFolder": "Pin This Folder", + "addSelected": "Add Selected ({n})", + "noPhysicalFilesToRename": "No physical files found to rename", + "copedToClipboardWithName": "Copied {name} to clipboard", + "cutToClipboardWithName": "Cut {name} to clipboard", + "deletedItem": "Deleted {name}", + "noItemsFound": "No {type} found", + "calculatingSizes": "Calculating sizes...", + "contains": "Contains", + "modified": "Modified", + "permissions": "Permissions", + "itemsSelected": "Items Selected", + "totalSize": "Total Size", + "selectedPaths": "", + "ftpServerNotification": "NFile FTP Server", + "ftpRunningAt": "Running at ftp://{ip}:{port}", + "ftpServerChannelName": "FTP Server", + "ftpServerChannelDesc": "Displays status of the background FTP Server", + "nfileAudioPlayer": "NFile Audio Player", + "nfileArchiveOperations": "NFile Archive Operations", + "archiveProgressDesc": "Shows progress of file compression and extraction", + "nfileStorage": "NFile Storage", + "internalStorageViaNFile": "Internal storage via NFile", + "webSharingServer": "Web Sharing Server", + "webSharingServerDesc": "Displays status of the background Web Sharing Server", + "nfileInternetWebShare": "NFile Internet Web Share", + "nfileLocalWebShare": "NFile Local Web Share", + "runningAt": "Running at {url}", + "nfileVersion": "NFile v1.0.43", + "storageAnalyzer": "Storage Analyzer", + "freeSpace": "{size}'", + "totalSpace": "{size}'", + "type": "Type", + "movedItemsSuccessfully": "Moved items successfully", + "copiedItemsSuccessfully": "Copied items successfully", + "archiveCreatedSuccessfully": "Archive \"{name}.{format}\" created successfully!", + "folderContains": "{f} subfolder(s), {d} file(s)", + "itemsSelectedCount": "{c} items ({f} folder(s), {d} file(s))", + "language": "Language", + "languageSub": "Select application language", + "systemDefault": "System default", + "spanish": "Spanish", + "english": "English", + "threeDotDisabledInfoSub": "Choose what to show on the right side of files and folders when 3-dot is hidden", + "appExitBehaviorSub": "Choose between exit confirmation dialog or double-pressing back button to exit", + "noSettingsFound": "No settings found", + "trySearchingAnotherKeyword": "Try searching for another keyword", + "settingsCategories": "Settings Categories", + "showConfirmationDialog": "Show confirmation dialog", + "vibrantOrange": "Vibrant Orange", + "royalPurple": "Royal Purple", + "emeraldGreen": "Emerald Green", + "crimsonRed": "Crimson Red", + "amberGold": "Amber Gold", + "cyberpunkPink": "Cyberpunk Pink", + "sapphireBlue": "Sapphire Blue", + "forestGreen": "Forest Green", + "sunsetPeach": "Sunset Peach", + "defaultLogo": "Default Logo", + "outfitModernSans": "Outfit Modern Sans", + "jetBrainsTechMono": "JetBrains Tech Mono", + "montserratUrbanSans": "Montserrat Urban Sans", + "customImportedFont": "Custom Imported Font", + "signatureDefaultFont": "Signature Default (Lexend Deca)", + "signatureDefaultFontDesc": "Original NFile clean geometric look", + "outfitFontDesc": "Super sleek, minimal, and premium geometric aesthetic", + "jetBrainsFontDesc": "Clean and futuristic developer monospaced look", + "montserratFontDesc": "Bold, modern, and striking typographic scale", + "customFontTitle": "Custom Font ({name})", + "customFontDesc": "Your custom loaded font file", + "replaceCustomFontFile": "Replace Custom Font File", + "importCustomFontFile": "Import Custom Font File (.ttf/.otf)", + "noneHideInfo": "None / Hide Info", + "noneHideInfoDesc": "Do not display additional information on the right side", + "dateTimeTitle": "Date & Time", + "dateTimeDesc": "Display the last modified date and time", + "fileSizeItemCount": "File Size / Item Count", + "fileSizeItemCountDesc": "Display file size for files and item count for folders", + "confirmDialogTitle": "Confirmation Dialog", + "confirmDialogDesc": "Prompt for exit verification before closing", + "doublePressToExit": "Double-Press to Exit", + "doublePressToExitDesc": "Tap the back button twice within a short window to exit", + "neverManuallyClean": "Never (Manually clean bin)", + "days7": "7 Days", + "days15": "15 Days", + "days30Recommended": "30 Days (Recommended)", + "trashDeletionWarning": "Items in the Recycle Bin will be permanently deleted after this duration.", + "fileExplorerAndNavigation": "File Explorer & Navigation", + "materialYouDynamic": "Material You (Dynamic Wallpaper Colors)", + "originalDefaultBlue": "Original Default (Signature Blue)", + "classicSolid": "Classic Solid (Material)", + "modernRounded": "Modern Rounded (Material)", + "starredSpecial": "Starred Special (Material)", + "snippetDocument": "Snippet Document (Material)", + "minimalOutlined": "Minimal Outlined (Material)", + "nfileBrokenOutline": "NFile Broken Outline (Default)", + "categoryGridVuesax": "Category Grid / Vuesax Grid", + "chooseTrailingInfoDesc": "Choose what is displayed on the right side of files and folders when the 3-dot action buttons are hidden.", + "chooseAppLauncherIconDesc": "Choose a custom logo for the application launcher icon. Note that some launchers may take a few seconds to update.", + "nothingDotMatrix": "Nothing Dot-Matrix & Sans", + "nothingDotMatrixDesc": "High-tech retro dot matrix headings + clean body", + "appTypographyTitle": "App Typography", + "selectTypefaceDesc": "Select a beautiful typeface to customize NFile's overall visual theme", + "doublePressBackToExit": "Double-press back button to exit", + "mediaAndDefaultActions": "Media & Default Actions", + "hamburgerClassicMenu": "Hamburger / Classic Menu", + "dotMatrixSans": "Dot-Matrix & Sans", + "neverAutoDeleteDisabled": "Never (Auto-delete disabled)", + "after1Day": "After 1 Day", + "afterNDays": "After {days} Days", + "uiOperationCancelled": "Operation Cancelled", + "uiCompressionLimitExceeded": "Compression Limit Exceeded", + "uiCompressingFiles": "Compressing Files", + "uiExtractingArchive": "Extracting Archive", + "uiExtremeSpeed": "Extreme Speed", + "uiStatelessCachingAsyncScans": "Stateless caching & async scans", + "uiVaultSecure": "Vault Secure", + "uiEncryptedSafeWorkspace": "Encrypted safe workspace", + "uiServersHub": "Servers Hub", + "uiFtpLanSftpWebdav": "FTP, LAN, SFTP & WebDAV", + "uiRichUi": "Rich UI", + "uiAmoledBlackBeautifulSeeds": "AMOLED Black & beautiful seeds", + "uiDeleteFile": "Delete File", + "uiNewFile": "New File", + "uiBestForTextDocuments": "Best for text documents", + "uiBestForBrochuresPhotos": "Best for brochures & photos", + "uiPageLayout": "Page Layout", + "uiScrollDirection": "Scroll Direction", + "uiDeletePermanentlyFromTheServer": "Delete \"${item.name}\" permanently from the server?", + "uiDownloadsFileLocalClipboard": "Downloads file → local clipboard", + "uiDownloadsAndDeletesFromServer": "Downloads and deletes from server", + "uiApplications": "Applications", + "uiImages": "Images", + "uiVideos": "Videos", + "uiAudio": "Audio", + "uiDocuments": "Documents", + "uiSystemOther": "System / Other", + "uiEgImage": "e.g. Image_#", + "uiEg3": "e.g. 3", + "uiEg1": "e.g. 1", + "uiExistingFile": "Existing File", + "uiDroppedFolder": "Dropped Folder", + "uiCurrentFolder": "Current Folder", + "uiMoveHere": "Move here", + "uiCutPasteItemIntoDestinationFolder": "Cut & paste item into destination folder", + "uiCopyHere": "Copy here", + "uiLeavesOriginalFileIntactAndDuplicatesHere": "Leaves original file intact and duplicates here", + "uiCompressItemIntoAZiptarArchiveHere": "Compress item into a zip/tar archive here", + "uiShowAllFilesAndFoldersInThisDirectory": "Show all files and folders in this directory", + "uiPdfsWordDocsSpreadsheetsTextsAndEbooks": "PDFs, Word docs, spreadsheets, texts, and e-books", + "uiJpegsPngsWebpsAndRawPhotoFormats": "JPEGs, PNGs, WebPs, and raw photo formats", + "uiMp3sWavsAacsAndHighfidelityAudios": "MP3s, WAVs, AACs, and high-fidelity audios", + "uiMp4sMkvsWebmsAndHighresVideoClips": "MP4s, MKVs, WebMs, and high-res video clips", + "uiZips7zsRarsAndOtherCompressedAssets": "ZIPs, 7Zs, RARs, and other compressed assets", + "uiName": "Name", + "uiPath": "Path", + "uiSize": "Size", + "deletePermanentlyFromServer": "Delete \"{name}\" permanently from the server?", + "uiPreparingFoldersForSharing": "Preparing folders for sharing...", + "uiCompressingContentsPleaseWait": "Compressing contents, please wait", + "uiCoreHighlights": "Core Highlights", + "uiConnectShare": "Connect & Share", + "uiNewlyCreatedOrDownloadedFilesWill": "Newly created or downloaded files will show up here.", + "uiSqliteDatabaseReader": "SQLite Database Reader", + "uiFailedToOpenDatabase": "Failed to open database", + "uiPk": "PK", + "uiNotNull": "NOT NULL", + "uiCreateANewDirectory": "Create a new directory", + "uiCreateANewEmptyTextDocument": "Create a new empty text document", + "uiNewArchive": "New Archive", + "uiCompressCurrentFolderContents": "Compress current folder contents", + "uiLayoutMode": "Layout Mode", + "uiListView": "List View", + "uiGridView": "Grid View", + "uiSizePaddingOptions": "Size & Padding Options", + "uiIconFolderSize": "Icon & Folder Size", + "uiItemPaddingSpacing": "Item Padding & Spacing", + "uiSortBy": "Sort By", + "uiOnlyThisFolder": "Only this folder", + "uiEnableCustomSortingSpecificToThis": "Enable custom sorting specific to this folder", + "uiStorageVolumes": "Storage Volumes", + "uiNetworkConnections": "Network Connections", + "uiNoResultsFound": "No results found", + "uiEmptyFolder": "Empty Folder", + "uiThisDirectoryDoesNotContainAny": "This directory does not contain any files or subfolders.", + "uiPastedHoldingClipboardForMultiplePastes": "Pasted (holding clipboard for multiple pastes)", + "uiFiles": "Files", + "uiPdfDisplaySettings": "PDF Display Settings", + "uiOptimizeRenderingPerformanceForLargeDesignheavy": "Optimize rendering performance for large, design-heavy, or scanned documents.", + "uiQuickPerformancePresets": "Quick Performance Presets", + "uiDetailedTuningOptions": "Detailed Tuning Options", + "uiDisableToSignificantlyBoostPageRendering": "Disable to significantly boost page rendering speed and eliminate scroll stutter.", + "uiNetworkStatus": "Network status", + "uiConnected": "Connected", + "uiServerAddress": "Server address", + "uiFailedToLoadImage": "Failed to load image", + "uiSelectStorageDrive": "Select Storage Drive", + "uiLongPressToOpenWith": "Long press to Open with...", + "uiAllItems": "All Items", + "uiFolders": "Folders", + "uiRemoteConnections": "Remote Connections", + "uiSelectNetworkService": "Select Network Service", + "uiMountARemoteServerOrNas": "Mount a remote server or NAS share as a dynamic drive within your NFile storage lists.", + "uiEnterConnectionDetailsToLinkThis": "Enter connection details to link this network volume.", + "uiCreatingMountPoint": "Creating Mount Point...", + "uiRecycleBinIsEmpty": "Recycle Bin is Empty", + "uiItemsYouDeleteWhenRecycleBin": "Items you delete when Recycle Bin is enabled will appear here. You can restore them or permanently delete them.", + "uiNewRemoteFolder": "New Remote Folder", + "uiConnectionLost": "Connection Lost", + "uiEmptyDirectory": "Empty Directory", + "uiChooseProtectionMode": "Choose Protection Mode", + "uiChooseHowYouWantToProtect": "Choose how you want to protect your selected files. Secured files are XOR scrambled instantly.", + "uiActive": "Active", + "uiHideFiles": "Hide Files", + "uiSecurityStorage": "SECURITY STORAGE", + "uiTotalSpaceSecured": "Total Space Secured", + "uiHiddenFiles": "Hidden Files", + "uiEstablishingSecureProxyRelay": "Establishing secure proxy relay...", + "uiScanQrCode": "Scan QR Code", + "uiWebSharingHub": "Web Sharing Hub", + "uiLocalWebShare": "Local Web Share", + "uiInternetShareLink": "Internet Share Link", + "uiHttpLocalShareServer": "HTTP Local Share Server", + "uiAllowsOtherDevicesOnTheSame": "Allows other devices on the same Wi-Fi to access, view, and stream your files in their web browser.", + "uiServerOnlineStreaming": "Server Online & Streaming", + "uiDirectBrowserUrl": "Direct Browser URL:", + "uiServerIsIdle": "Server is Idle", + "uiMakeSureOtherDevicesAreOn": "Make sure other devices are on the same Wi-Fi network as this device, then start the server.", + "uiInternetShareTunnel": "Internet Share Tunnel", + "uiGeneratesASecureTemporaryPublicTunnel": "Generates a secure temporary public tunnel link. Share this link with anyone anywhere on the internet to let them download files high-speed, no matter the file size.", + "uiCloudTunnelActive": "Cloud Tunnel Active", + "uiTemporaryShareLinkActive24h": "Temporary Share Link (Active 24h):", + "uiConnectedBrowserClients": "Connected Browser Clients", + "uiWaitingForIncomingInternetDownloads": "Waiting for incoming internet downloads...", + "uiInternetSharingInactive": "Internet Sharing Inactive", + "uiActivateTheTunnelToEstablishA": "Activate the tunnel to establish a secure link that works beyond local Wi-Fi.", + "uiLosslessAudio": "Lossless Audio", + "uiNoSynchronizedLyricsFound": "No Synchronized Lyrics Found", + "uiKeepALrcFileWithThe": "Keep a .lrc file with the exact same name next to your song, or select it manually below.", + "uiTapALineToSeekPlayback": "Tap a line to seek playback", + "uiAppManager": "App Manager", + "uiExactStorageCalculation": "Exact Storage Calculation", + "uiToSeeExactAppStorageSizes": "To see exact app storage sizes (APK + data + cache) instead of just the raw installer size, please enable the Usage Access permission for NFile in System Settings.", + "uiGrantUsageAccessPermission": "Grant Usage Access Permission", + "uiStorageAnalytics": "Storage Analytics", + "uiScanningDeviceStorage": "Scanning Device Storage", + "uiAnalyzingFilesCategorizingAssetsAndReading": "Analyzing files, categorizing assets, and reading installed apps space...", + "uiTotalStorage": "Total Storage", + "uiBreakdown": "Breakdown", + "uiNoApplicationsFound": "No applications found", + "uiNoBackupsFound": "No backups found", + "uiSlideTapToUnlock": "Slide / Tap to Unlock", + "uiHwDec": "HW Dec", + "uiOverallProgress": "Overall Progress", + "uiRenamingFiles": "Renaming files...", + "uiPleaseWaitUpdatingFolderContent": "Please wait, updating folder content", + "uiBatchRename": "Batch Rename", + "uiRenamePreview": "Rename Preview", + "uiBackToEdit": "Back to Edit", + "uiApplyChanges": "Apply Changes", + "uiFileAlreadyExists": "File Already Exists", + "uiApplyToAllRemainingConflicts": "Apply to all remaining conflicts", + "uiNewer": "Newer", + "uiDragDropOptions": "Drag & Drop Options", + "uiDestinationLocation": "Destination Location", + "uiChooseAction": "Choose Action", + "uiExtractArchive": "Extract Archive", + "uiFilterFilesByType": "Filter Files By Type", + "uiSelectACategoryToDisplayMatching": "Select a category to display matching files only", + "uiNoMatchingDirectoriesOrFilesFound": "No matching directories or files found", + "uiBuiltinNfileViewer": "Built-in NFile Viewer", + "uiSystemExternalApp": "System External App", + "uiOpenWithThirdPartyAppsOn": "Open with third party apps on device", + "uiSearchInTab": "Search in tab", + "uiInternalStorage": "Internal Storage", + "uiBrowseDeviceFiles": "Browse device files", + "uiCustomize": "Customize", + "uiNoShortcutsPinnedTapCustomizeTo": "No shortcuts pinned. Tap Customize to add.", + "uiDragItemsByTheHandleTo": "Drag items by the handle (=) to reorder icons on the Home Screen.", + "uiDefaultScanLocations": "Default Scan Locations:", + "uiCustomScanLocations": "Custom Scan Locations:", + "uiNoCustomPathsAdded": "No custom paths added.", + "uiRecentFiles": "Recent Files", + "uiViewAll": "View All", + "uiRestrictedSystemFolder": "Restricted System Folder", + "uiAndroid11RestrictsStandardAccessTo": "Android 11+ restricts standard access to Android/data and Android/obb folders to protect app data. To view and modify these files, NFile requires advanced permissions.", + "errorShizukuNotRunning": "Access denied. Shizuku is not running or authorized.", + "vaultEnterPin": "Enter PIN to Unlock Wallet", + "vaultSetPin": "Set your 4-digit Wallet PIN", + "vaultConfirmPin": "Confirm your 4-digit PIN", + "vaultPinSuccess": "PIN Set Successfully!", + "vaultPinMismatch": "PINs do not match. Try again!", + "vaultPinIncorrect": "Incorrect PIN. Try again!", + "actionStop": "Stop", + "actionStart": "Start", + "actionAnonymous": "Anonymous", + "sharingDirectory": "Sharing Directory: {dir}", + "stopWebServer": "Stop Web Server", + "startWebServer": "Start Web Server", + "protocolDescSmb": "Local Area Network & SMB NAS Share", + "protocolDescFtp": "Standard File Transfer Protocol", + "protocolDescSftp": "SSH Secure File Transfer Server", + "protocolDescWebDav": "HTTP Web Distributed Authoring", + "protocolDescSaf": "Android Storage Access Framework (SD Card / External)", + "sortNameAsc": "Name (A-Z)", + "sortNameDesc": "Name (Z-A)", + "sortNewest": "Newest", + "sortOldest": "Oldest", + "sortSizeLarge": "Size (Large)", + "sortSizeSmall": "Size (Small)", + "sortType": "Type", + "webSharingDirectory": "Sharing Directory: {dir}", + "aboutCopyright": "Copyright © 2026 NFile. All rights reserved.", + "aboutDescription": "NFile is a beautiful, fluid, and open-source file manager and offline media hub built with Flutter. Designed for extreme performance, clean glassmorphic aesthetics, and seamless user experiences.", + "aboutMadeWith": "Made with ❤️ by Rubex", + "aboutVersion": "v1.0.42 (Stable)", + "stepXofY": "Step {step} of {total}", + "connectingMessage": "Please wait while we establish a reliable pathway to the {type} server.", + "documentsUiWarning": "Your device does not have a default System Files/Documents app (DocumentsUI) enabled, ", + "deletedLabel": "Deleted: {date} • {size}", + "originalPathLabel": "Original Path: {path}", + "configuringItems": "Configuring {count} items", + "reviewingItems": "Reviewing {count} items", + "copiedItems": "Copied {count} item(s)", + "cutItems": "Cut {count} item(s)", + "protectedItems": "Protected {count} items successfully.", + "noMatchesFound": "We could not find anything matching ", + "filesCount": "files: {count}", + "foldersCount": "folders: {count}", + "queryReturned": "Query returned {count} rows", + "showingRows": "Showing {start} - {end}", + "processingItem": "Processing item {current} of {total}", + "compressionLevel": "Compression Level: {label}", + "videoCodecInfo": "AVC / AAC • 1080p", + "scanQrMessage": "Scan with another device to open {type} immediately.", + "conflictFileExists": "A file named ", + "appSizeInfo": "Size: {size} • Installed: {date}", + "backupSizeInfo": "Size: {size}", + "webSearchPlaceholder": "Search files & folders...", + "webUploadBtn": "Upload", + "webUploadTooltip": "Upload Files to this Folder", + "webParentDir": ".. (Parent Directory)", + "webGoUpLevel": "Go up one level", + "webNoResults": "No items match your search", + "webCheckSpelling": "Check the spelling or try a different search term.", + "webFileName": "File Name", + "webCopyLink": "Copy Link", + "webDownload": "Download", + "webCloseModal": "Close Modal", + "webDownloadFile": "Download File", + "webDropFiles": "Drop files here to upload", + "webUploadInstantly": "Your files will be uploaded instantly to this shared folder", + "webUploadingFile": "Uploading file...", + "webSecurelySharing": "Securely sharing and streaming files via NFile", + "webUploadSuccess": "Upload completed successfully!", + "webLinkCopied": "Link copied to clipboard!", + "webLinkCopyFailed": "Failed to copy link.", + "webUploadingName": "Uploading \\${file.name}...", + "webUploadFailedName": "Failed to upload \\${file.name}", + "webLoadingPreview": "Loading preview...", + "webStreamFailed": "Failed to stream document. You can still download it directly.", + "webPreviewNotSupported": "Preview is not supported for this file type", + "webClickDownload": "Click Download below to save it on your system.", + "webVideoNotSupported": "Your browser does not support the video streaming tag.", + "webAudioNotSupported": "Your browser does not support the audio element.", + "webUploadFailed": "Upload failed", + "webNetworkError": "Network error" +} \ No newline at end of file diff --git a/assets/i18n/es.json b/assets/i18n/es.json new file mode 100644 index 0000000..d054ee7 --- /dev/null +++ b/assets/i18n/es.json @@ -0,0 +1,868 @@ +{ + "cancel": "Cancelar", + "ok": "Aceptar", + "save": "Guardar", + "delete": "Eliminar", + "rename": "Renombrar", + "copy": "Copiar", + "cut": "Cortar", + "paste": "Pegar", + "share": "Compartir", + "extract": "Extraer", + "archive": "Comprimir", + "close": "Cerrar", + "done": "Hecho", + "back": "Atrás", + "exit": "Salir", + "home": "Inicio", + "browse": "Explorar", + "search": "Buscar", + "selectAll": "Seleccionar Todo", + "refresh": "Actualizar", + "properties": "Propiedades", + "info": "Información", + "more": "Más", + "preview": "Vista Previa", + "create": "Crear", + "open": "Abrir", + "edit": "Editar", + "upload": "Subir", + "download": "Descargar", + "connect": "Conectar", + "disconnect": "Desconectar", + "restore": "Restaurar", + "clear": "Limpiar", + "skip": "Omitir", + "replace": "Reemplazar", + "keepBoth": "Mantener Ambos", + "uninstall": "Desinstalar", + "backup": "Respaldo", + "confirm": "Confirmar", + "appTitle": "NFile", + "appSubtitle": "Suite Multimedia Bella", + "grantPermission": "Conceder Permiso", + "storageAccessRequired": "Acceso a Almacenamiento Requerido", + "storagePermissionMessage": "NFile requiere permiso de almacenamiento para administrar, organizar y mostrar tus archivos multimedia sin problemas.", + "openingSharedDocument": "Abriendo documento compartido...", + "resolvingSecureContent": "Resolviendo flujo de contenido seguro", + "myFiles": "Mis Archivos", + "refreshDashboard": "Actualizar Panel", + "dashboardRefreshed": "Panel actualizado correctamente", + "exitApplication": "Salir de la Aplicación", + "exitConfirmation": "Confirmar Salida", + "exitConfirmationMessage": "¿Estás seguro de que quieres salir? Presiona atrás de nuevo o toca Salir para cerrar la aplicación.", + "pressBackAgain": "Presiona atrás de nuevo para salir", + "confirmDeletion": "Confirmar Eliminación", + "deletePermanently": "Eliminar Permanentemente", + "deletePermanentlyQuestion": "¿Eliminar Permanentemente?", + "deleteSelectedItems": "Eliminar Elementos Seleccionados", + "deleteSourceFiles": "Eliminar archivos de origen al finalizar", + "permanentlyDeleteItems": "¿Estás seguro de que quieres eliminar permanentemente {count} elemento(s)? Esta acción no se puede deshacer.", + "permanentlyDeleteItemsArchive": "¿Estás seguro de que quieres eliminar precisamente {count} elemento(s) del archivo? Esto no se puede deshacer.", + "deletedSuccessfully": "Eliminado correctamente {count}", + "permanentlyDeleted": "Eliminado permanentemente {count} elemento(s)", + "failedToDelete": "Error al eliminar elementos", + "errorDeleting": "Error al eliminar: {e}", + "itemsDeleted": "Elementos eliminados correctamente", + "recycleBin": "Papelera de Reciclaje", + "emptyRecycleBin": "Vaciar Papelera", + "emptyRecycleBinQuestion": "¿Vaciar Papelera?", + "emptyBin": "Vaciar Papelera", + "recycleBinEmptied": "Papelera vaciada correctamente", + "errorEmptyingBin": "Error al vaciar papelera: {e}", + "emptyRecycleBinMessage": "¿Estás seguro de que quieres eliminar permanentemente todos los elementos de la Papelera? Esta acción es irreversible.", + "deletePermanentlyRecycleMessage": "¿Estás seguro de que quieres eliminar permanentemente {count} elemento(s)? Esta acción no se puede deshacer.", + "select": "{n} Seleccionado(s)", + "searchDeletedFiles": "Buscar archivos eliminados...", + "restoredItems": "Restaurado {count} elemento(s) correctamente", + "errorRestoring": "Error al restaurar elementos: {e}", + "moreSettings": "Más Ajustes", + "searchSettings": "Buscar ajustes...", + "generalAndBehavior": "General y Comportamiento", + "generalAndBehaviorSub": "Pantalla predeterminada, controles de navegación y accesos directos", + "appearanceAndThemes": "Apariencia y Temas", + "appearanceAndThemesSub": "Temas, iconos de aplicación, estilos de carpeta y tipografía", + "fileExplorerOptions": "Opciones del Explorador", + "fileExplorerOptionsSub": "Barra de direcciones, archivos ocultos, pestañas y arrastrar y soltar", + "listAndLayout": "Estilo de Lista y Diseño", + "listAndLayoutSub": "Tamaños de carpeta, conteos y formatos de fecha/hora", + "mediaPreferences": "Preferencias de Medios", + "mediaPreferencesSub": "Vista de álbum predeterminada y vistas previas en miniatura", + "fileActionsAndViewers": "Acciones de Archivos y Visores", + "fileActionsAndViewersSub": "Acciones de apertura y configuración de visores predeterminados", + "recycleBinTrash": "Papelera de Reciclaje", + "recycleBinTrashSub": "Opciones de papelera y duración de eliminación automática", + "backupAndRestore": "Respaldo y Restauración", + "backupAndRestoreSub": "Respaldar tu configuración en un archivo JSON o restaurarla", + "defaultToBrowseScreen": "Predeterminar a Pantalla de Exploración", + "defaultToBrowseScreenSub": "Iniciar directamente en el explorador de almacenamiento al abrir la aplicación", + "rememberLastFolder": "Recordar Última Carpeta Abierta", + "rememberLastFolderSub": "Abrir la última carpeta explorada al iniciar la aplicación", + "showHomeBrowseBar": "Mostrar Barra Inferior Inicio/Explorar", + "showHomeBrowseBarSub": "Alternar visibilidad de la barra de navegación inferior en la pantalla de Inicio", + "hideNavLabels": "Ocultar Etiquetas de Navegación", + "hideNavLabelsSub": "Ocultar etiquetas de texto de la barra inferior (Inicio/Explorar) para un aspecto más limpio y compacto", + "hideAndroidNavBar": "Ocultar Barra de Navegación Android", + "hideAndroidNavBarSub": "Ocultar barra de navegación inferior para maximizar el espacio de pantalla (deslizar hacia arriba la muestra)", + "showBottomNavBar": "Mostrar Barra de Acción Inferior", + "showBottomNavBarSub": "Habilitar barra de acción inferior en la pantalla de Exploración", + "hideActionBarLabels": "Ocultar Etiquetas de Barra de Acción", + "hideActionBarLabelsSub": "Mostrar solo iconos en la barra de acción de selección inferior", + "customizeShortcuts": "Personalizar Accesos Directos", + "customizeShortcutsSub": "Reordenar y alternar visibilidad de elementos de categorías rápidas", + "showRecentFiles": "Mostrar Archivos Recientes", + "showRecentFilesSub": "Mostrar la lista de archivos accedidos recientemente en la pantalla de Inicio", + "preventLeftBackGesture": "Prevenir Gesto de Retroceso Izquierdo para el Cajón", + "preventLeftBackGestureSub": "Excluye el borde izquierdo de la pantalla de los gestos de retroceso de Android, facilitando abrir el cajón. Aún puedes deslizar desde el borde derecho para volver.", + "appExitBehavior": "Comportamiento de Salida", + "accentColorTheme": "Color de Acento / Tema Dinámico", + "folderIconStyle": "Estilo de Icono de Carpeta", + "appDrawerButtonStyle": "Estilo del Botón del Cajón", + "amoledBlackMode": "Modo Negro AMOLED", + "amoledBlackModeSub": "Usar fondo negro puro en Modo Oscuro para pantallas AMOLED", + "appIcon": "Icono de Aplicación", + "appTypography": "Tipografía / Familia de Fuente", + "useMaterialIcons": "Usar Iconos Material Expresivos", + "useMaterialIconsSub": "Reemplazar iconos Broken personalizados con iconos estándar de Material Design", + "showAddressBar": "Mostrar Barra de Direcciones", + "showAddressBarSub": "Mostrar una barra de direcciones editable estilo Windows Explorer en la parte superior de la lista de archivos", + "showFloatingButton": "Mostrar Botón Flotante '+'", + "showFloatingButtonSub": "Habilitar botón de creación rápida (+) en la parte inferior de la pantalla de Exploración", + "showHiddenFiles": "Mostrar Archivos Ocultos", + "showHiddenFilesSub": "Muestra archivos de sistema y carpetas que empiezan con punto (.)", + "highlightExitedFolder": "Resaltar Carpeta de Salida", + "highlightExitedFolderSub": "Destellar brevemente y desplazarse a la carpeta de la que acabas de salir al volver", + "enableMultipleTabs": "Habilitar Múltiples Pestañas", + "enableMultipleTabsSub": "Permitir abrir múltiples carpetas en pestañas separadas para navegación rápida", + "enableSplitScreen": "Habilitar Pantalla Dividida", + "enableSplitScreenSub": "Explorar dos directorios lado a lado y transferir archivos fácilmente", + "enableDragAndDrop": "Habilitar Arrastrar y Soltar", + "enableDragAndDropSub": "Mantén presionado y arrastra carpetas o archivos para moverlos a otras carpetas", + "confirmDragDrop": "Confirmar Acciones de Arrastrar y Soltar", + "confirmDragDropSub": "Mostrar ventana de opciones (Copiar, Mover, Comprimir) al soltar archivos", + "showFolderFileCount": "Mostrar Encabezado de Conteo de Carpetas/Archivos", + "showFolderFileCountSub": "Mostrar total de carpetas y archivos bajo la barra de título de almacenamiento", + "showFolderContentCount": "Mostrar Conteo de Contenido de Carpeta", + "showFolderContentCountSub": "Calcular y mostrar total de archivos y carpetas dentro de los listados de directorios", + "showFolderSize": "Mostrar Tamaño de Carpeta", + "showFolderSizeSub": "Calcular y mostrar el tamaño total de todos los archivos dentro de los directorios (puede afectar el rendimiento del listado)", + "use24HourFormat": "Usar Formato de Hora 24h", + "use24HourFormatSub": "Alternar entre formato de 12 horas (AM/PM) y 24 horas en todas las listas", + "hideTimeDate": "Ocultar Hora y Fecha de las Listas", + "hideTimeDateSub": "Ocultar completamente fechas y horas de modificación debajo de archivos y carpetas", + "adaptiveMultiLine": "Nombres de Archivo Multilínea Adaptativos", + "adaptiveMultiLineSub": "Permitir que los nombres de archivo se envuelvan en 3 líneas en lugar de truncarse", + "hide3DotButtons": "Ocultar Botones de 3 Puntos", + "hide3DotButtonsSub": "Ocultar el botón de menú de tres puntos junto a carpetas y archivos", + "threeDotDisabledInfo": "Información de 3 Puntos Deshabilitada", + "defaultAlbumView": "Vista Preferida de Álbum", + "defaultAlbumViewSub": "Abrir categorías rápidas de Imágenes/Videos directamente en vista preferida de Carpetas (Álbumes)", + "showMediaPreviews": "Mostar Vistas Previas de Medios", + "showMediaPreviewsSub": "Mostrar miniaturas de imágenes y videos en lugar de iconos de archivo genéricos", + "skipOpenWithDialog": "Omitir Diálogo \"Abrir Con\"", + "skipOpenWithDialogSub": "Evitar el diálogo de elección de aplicación y abrir archivos directamente con visores predeterminados", + "resetDefaultViewers": "Restablecer Visores de Archivos Predeterminados", + "resetDefaultViewersSub": "Limpiar todas las asociaciones \"Abrir Con\" recordadas para visores de archivos", + "viewerChoicesReset": "Todas las opciones de visor predeterminado han sido restablecidas", + "enableRecycleBin": "Habilitar Papelera de Reciclaje", + "enableRecycleBinSub": "Mover archivos y carpetas eliminados a una Papelera oculta en lugar de eliminarlos permanentemente", + "autoDeleteTrashDuration": "Duración de Eliminación Automática", + "backupSettings": "Config. de Copia de Seguridad", + "backupSettingsSub": "Guardar toda tu configuración actual en NFile/Backups/Settings/", + "restoreSettings": "Restaurar Configuración", + "restoreSettingsSub": "Seleccionar y restaurar configuración desde un archivo backup JSON", + "settingsBackedUp": "Ajustes respaldados en NFile/Backups/Settings/nfile_settings_backup.json", + "settingsRestored": "¡Ajustes restaurados correctamente!", + "failedToBackup": "Error al respaldar ajustes: {e}", + "failedToRestore": "Error al restaurar ajustes: {e}", + "chooseTrailingInfoStyle": "Elegir Estilo de Información Final", + "chooseExitBehavior": "Elegir Comportamiento de Salida", + "chooseAccentTheme": "Elegir Tema de Acento", + "chooseFolderIconStyle": "Elegir Estilo de Icono de Carpeta", + "chooseDrawerButtonStyle": "Elegir Estilo de Botón del Cajón", + "appIconPicker": "Selector de Icono de Aplicación", + "appLauncherIcon": "Icono de Lanzador de Aplicación", + "logo": "Logo", + "logo1": "Logo 1", + "logo2": "Logo 2", + "logo3": "Logo 3", + "logo4": "Logo 4", + "appIconSwitched": "¡Icono de aplicación cambiado a {title} correctamente!", + "customFontLoaded": "Fuente personalizada cargada", + "failedToLoadFont": "Error al cargar el archivo de fuente seleccionado.", + "invalidFileType": "Tipo de Archivo No Válido", + "invalidFileTypeMessage": "Por favor selecciona un archivo de fuente OpenType (.otf) o TrueType (.ttf) válido.", + "removeCustomFont": "Eliminar Fuente Personalizada", + "customFontRemoved": "Fuente personalizada eliminada.", + "pleaseSelectValidBackup": "Por favor selecciona un archivo de respaldo .json válido", + "systemAppDisabled": "Aplicación del Sistema Deshabilitada", + "failedToRequestSaf": "Error al solicitar carpeta SAF: {e}", + "enterConnectionName": "Por favor ingresa un nombre de conexión", + "enterServerAddress": "Por favor ingresa dirección del servidor / nombre de host", + "connectionFailed": "Conexión fallida: {e}", + "http": "HTTP", + "httpsSecure": "HTTPS (Seguro)", + "retryConnection": "Reintentar Conexión", + "uploadClipboardHere": "Subir Portapapeles Aquí", + "uploadLocalClipboard": "Subir portapapeles local al servidor", + "newFolder": "Nueva Carpeta", + "folderName": "Nombre de carpeta", + "copyToLocalDevice": "Copiar a Dispositivo Local", + "moveToLocalDevice": "Mover a Dispositivo Local", + "deleteQuestion": "Eliminar", + "navigation": "Navegación", + "systemRoot": "Raíz del Sistema", + "globalSearch": "Búsqueda Global", + "serversAndTools": "Servidores y Herramientas", + "privateWallet": "Billetera Privada", + "ftpServer": "Servidor FTP", + "webSharing": "Compartir Web", + "addRemoteConnection": "Agregar Conexión Remota", + "quickCategories": "Categorías Rápidas", + "addShortcut": "Añadir Acceso Directo", + "customizationAndSettings": "Personalización y Ajustes", + "lightMode": "Modo Claro", + "darkMode": "Modo Oscuro", + "aboutNFile": "Acerca de NFile", + "couldNotOpenLink": "No se pudo abrir el enlace: {url}", + "starOnRepository": "Estrella en el Repositorio", + "joinTelegram": "Unirse al Canal de Telegram", + "shareAppWithFriends": "Compartir App con Amigos", + "exploreGitHubSource": "Explorar Código Fuente en GitHub", + "copedToClipboard": "Copiado al portapapeles", + "cutToClipboard": "Cortado al portapapeles", + "copedNItems": "Copiado {n} elemento(s)", + "cutNItems": "Cortado {n} elemento(s)", + "copedToClipboardN": "Copiado {n} elementos al portapapeles", + "copedLabelToClipboard": "Copiado {label} al portapapeles", + "cutToClipboardN": "Cortado {n} elementos al portapapeles", + "copedSelected": "Elementos seleccionados copiados", + "cutSelected": "Elementos seleccionados cortados", + "pastedSuccessfully": "Pegado correctamente", + "pastedNItems": "Pegado {n} elemento(s)", + "pastedItemsTo": "Pegado {count} elementos en {dest}", + "noShareableItems": "No se encontraron elementos compartibles.", + "noFilesToShare": "No hay archivos disponibles para compartir", + "errorSharing": "Error al compartir: {e}", + "errorPreparingFiles": "Error al preparar archivos para compartir: {e}", + "errorReadingSharedFile": "Error al leer archivo compartido: {e}", + "fileNotFoundOrNotShareable": "Archivo no encontrado o no compartible.", + "cannotMoveIntoItself": "No se puede mover una carpeta dentro de sí misma o a la misma ubicación", + "cannotCopyIntoItself": "No se puede copiar una carpeta dentro de sí misma o a la misma ubicación", + "movedSuccessfully": "{name} movido correctamente", + "copedSuccessfully": "{name} copiado correctamente", + "failedToMove": "Error al mover elemento: {e}", + "failedToCopy": "Error al copiar elemento: {e}", + "failedToTransfer": "Error al transferir: {e}", + "failedToConnectRemote": "Error al conectar al servidor remoto: {e}", + "pasteHere": "Pegar Aquí", + "pasteHereN": "Pegar Aquí ({n})", + "actionCancelled": "Acción cancelada / Portapapeles limpiado", + "extractToCurrentFolder": "Extraer a Carpeta Actual", + "addFile": "Agregar Archivo", + "addCustomPath": "Agregar Ruta Personalizada", + "customPaths": "{n} ruta(s) personalizada(s)", + "addFolderFileShortcut": "Agregar Acceso Directo a Carpeta/Archivo", + "customPathsTooltip": "Rutas Personalizadas", + "deleteShortcut": "Eliminar Acceso Directo", + "restoreLocation": "Restaurar Ubicación", + "excludeLocation": "Excluir Ubicación", + "addNetworkConnection": "Agregar Conexión de Red", + "removeConnection": "Eliminar Conexión", + "selectMode": "Modo Selección", + "viewAndSortOptions": "Opciones de Vista y Orden", + "storageVolumes": "Volúmenes de Almacenamiento y Tarjeta SD", + "createNew": "Crear Nuevo", + "openWith": "Abrir con...", + "justOnce": "Solo una vez", + "always": "Siempre", + "openWithApp": "Abrir con Aplicación", + "shareComingSoon": "Compartir próximamente", + "savedSuccessfully": "Guardado correctamente", + "errorSaving": "Error al guardar: {e}", + "errorLoading": "Error al cargar: {e}", + "standardMode": "Modo Estándar", + "lagFreeMode": "Modo Sin Retraso", + "continuous": "Continuo", + "singlePage": "Página Única", + "vertical": "Vertical", + "horizontal": "Horizontal", + "enableTextSelection": "Habilitar Selección de Texto", + "displaySettings": "Ajustes de Visualización", + "emptySheet": "Hoja Vacía", + "htmlPreview": "Vista Previa HTML", + "markdownPreview": "Vista Previa Markdown", + "reload": "Recargar", + "selectSyntax": "Seleccionar Sintaxis", + "findReplace": "Buscar / Reemplazar", + "saveFile": "Guardar Archivo", + "moreOptions": "Más Opciones", + "defaultZoom": "Zoom Predeterminado ({pt})", + "syntax": "Sintaxis ({lang})", + "find": "Buscar...", + "replaceWith": "Reemplazar con...", + "replaceAll": "Reemplazar Todo", + "undo": "Deshacer", + "redo": "Rehacer", + "fileSaved": "Archivo guardado correctamente", + "errorLoadingFile": "Error al cargar archivo: {e}", + "errorSavingFile": "Error al guardar archivo: {e}", + "replacedOccurrences": "Reemplazado {n} ocurrencias", + "ftpServerStarted": "Servidor FTP iniciado en ftp://{ip}:{port}", + "ftpServerStopped": "Servidor FTP detenido correctamente", + "errorStartingFtp": "Error al iniciar Servidor FTP: {e}", + "stopServerBeforeConfig": "Por favor detén el servidor antes de cambiar la configuración", + "changePort": "Cambiar Puerto", + "portNumber": "Número de Puerto", + "portHint": "ej. 9999", + "invalidPort": "Número de puerto no válido", + "setUsername": "Establecer Usuario", + "username": "Usuario", + "usernameCannotBeEmpty": "El nombre de usuario no puede estar vacío", + "stopServerBeforeEditing": "Detén el servidor antes de editar la configuración", + "ftpShortcutAdded": "¡Acceso directo al Servidor FTP agregado a la pantalla de inicio!", + "changeDirectory": "Cambiar directorio", + "changePortOption": "Cambiar puerto", + "setUser": "Establecer usuario", + "anonymousAccess": "Acceso anónimo", + "createShortcut": "Crear acceso directo", + "homeDirectory": "Directorio de inicio", + "userName": "Nombre de usuario", + "showHiddenFilesFtp": "Mostrar archivos ocultos", + "ftpes": "FTPES", + "ftpesDescription": "Conexión FTP segura sobre TLS explícito", + "webSharingStarted": "¡Servidor de Compartición HTTP Local iniciado! URL: {url}", + "webSharingStopped": "Servidor de Compartición HTTP Local detenido.", + "errorStartingWeb": "Error al iniciar Servidor HTTP: {e}", + "internetCloudTunnel": "¡Túnel de nube de Internet en línea! Enlace temporal activo.", + "failedToStartCloud": "Error al iniciar Compartición en Nube: {e}", + "linkCopied": "¡Enlace copiado al portapapeles!", + "internetShareDeactivated": "Túnel de Compartición por Internet desactivado.", + "copyUrl": "Copiar URL", + "qrCode": "Código QR", + "copyLink": "Copiar Enlace", + "noRecentFiles": "Sin archivos recientes", + "successfullyDeleted": "Elementos eliminados correctamente", + "folderIsEmpty": "La carpeta está vacía", + "couldNotReadArchive": "No se pudo leer el archivo", + "extractedItem": "Extraído {name} a {dest}", + "copiedPhysicalItems": "{n} elemento(s) copiado(s) al portapapeles", + "addedSuccessfully": "Agregado correctamente {n} elemento(s) al archivo", + "pastedCountItems": "Pegado {n} elemento(s) en el archivo", + "createArchive": "Crear Archivo", + "archiveName": "Nombre del Archivo", + "archiveFormat": "Formato del Archivo", + "passwordOptional": "Contraseña (Opcional)", + "splitVolumeSize": "Tamaño de Volumen Dividido en MB (Opcional)", + "leaveEmptyForSingle": "Dejar vacío para archivo único", + "createSeparateArchive": "Crear archivo separado para cada archivo", + "createArchiveFailed": "Error al crear archivo: {e}", + "extractToFolder": "Extraer a Carpeta", + "passwordIfEncrypted": "Contraseña (si está encriptado)", + "cancelPaste": "Cancelar Pegado", + "renameFile": "Renombrar Archivo", + "newFilename": "Nuevo nombre de archivo", + "namePattern": "Patrón de Nombre", + "extensionLabel": "Extensión", + "padding": "Relleno", + "startNumber": "Número de Inicio", + "findText": "Buscar texto", + "searchTerm": "Término de búsqueda", + "replacement": "Reemplazo", + "originalName": "Nombre original (%)", + "sequentialNumber": "Número secuencial (#)", + "tripleSequentialNumber": "Número secuencial triple (###)", + "fileNameWithoutExtension": "Nombre de archivo sin extensión ({n})", + "extensionWithDot": "Extensión con punto ({de})", + "extensionWithoutDot": "Extensión sin punto ({e})", + "fullNameWithExtension": "Nombre completo con extensión ({N})", + "background": "Fondo", + "cancelOperation": "Cancelar Operación", + "transferSpeed": "Velocidad de Transferencia", + "estTime": "Tiempo Estimado", + "dataProcessed": "Datos Procesados", + "showInLocation": "Mostrar en ubicación", + "copySelected": "Copiar Seleccionado", + "cutSelectedB": "Cortar Seleccionado", + "archiveCompress": "Comprimir (Archivo)", + "propertiesAndInfo": "Propiedades e Información", + "deleteSelected": "Eliminar Seleccionados", + "enterAbsolutePath": "Ingresar ruta absoluta...", + "pathNotFound": "Ruta no encontrada: {path}", + "copedPath": "Copiado: {path}", + "goToParentDirectory": "Ir al Directorio Padre", + "searchEllipsis": "Buscar...", + "useRootAccess": "Usar Acceso Root (Superusuario)", + "grantShizukuAccess": "Conceder Acceso Shizuku (Sin Root)", + "howToSetupShizuku": "¿Cómo configurar Shizuku?", + "newTab": "Nueva Pestaña", + "duplicateTab": "Duplicar Pestaña", + "closeOtherTabs": "Cerrar Otras Pestañas", + "closeTab": "Cerrar Pestaña", + "allFiles": "Todos los Archivos", + "documentsOnly": "Solo Documentos", + "imagesOnly": "Solo Imágenes", + "audioOnly": "Solo Audio", + "videosOnly": "Solo Videos", + "archivesOnly": "Solo Archivos", + "extractingBundle": "Extrayendo paquete para instalación...", + "noInstallableApk": "No se encontró APK instalable en el paquete", + "failedToExtractBundle": "Error al extraer paquete: {e}", + "failedToTriggerInstaller": "Error al iniciar instalador de APK dividido", + "sortBySize": "Ordenar por Tamaño", + "sortAlphabetically": "Ordenar Alfabéticamente", + "rescanStorage": "Reescanear Almacenamiento", + "selectAll_": "Seleccionar Todo", + "refreshList": "Actualizar Lista", + "uninstallAppsTitle": "Desinstalar Aplicaciones", + "confirmUninstallApps": "¿Estás seguro de que quieres desinstalar {n} aplicación(es) seleccionada(s)?", + "backingUpApps": "Respaldando aplicaciones seleccionadas...", + "backedUpApps": "Respaldado correctamente {n} aplicación(es) en NFile/Backups/Apps/", + "failedToBackupApps": "Error al respaldar algunas aplicaciones: {e}", + "launchApplication": "Iniciar Aplicación", + "systemSettingsDetails": "Ajustes del Sistema / Detalles", + "backUpApk": "Respaldar APK", + "backingUpApk": "Respaldando APK...", + "shareApkFile": "Compartir Archivo APK", + "uninstallApplication": "Desinstalar Aplicación", + "restoreInstallApp": "Restaurar / Instalar App", + "shareBackupFile": "Compartir Archivo de Respaldo", + "deleteBackupFile": "Eliminar Archivo de Respaldo", + "newestFirst": "Más Reciente Primero", + "oldestFirst": "Más Antiguo Primero", + "dateWise": "Por Fecha", + "newestFirstGrouped": "Más Reciente Primero (Agrupado por mes)", + "oldestFirstGrouped": "Más Antiguo Primero (Agrupado por mes)", + "sizeLargeFirst": "Tamaño (Grande Primero)", + "sizeSmallFirst": "Tamaño (Pequeño Primero)", + "lockOption": "Opción de Bloqueo", + "secureImport": "Importación Segura (Sandbox)", + "inPlaceScramble": "Codificación en Sitio (Rápido)", + "scramblingAndProtecting": "Codificando y Protegiendo...", + "restored": "Restaurado", + "failedToRestoreFile": "Error al restaurar archivo: {e}", + "fileDeletedPermanently": "Archivo eliminado permanentemente.", + "failedToDeleteFile": "Error al eliminar archivo: {e}", + "decryptingSecurely": "Descifrando de forma segura...", + "failedToDecrypt": "Error al descifrar y abrir elemento: {e}", + "securityDetails": "Detalles de Seguridad", + "errorLoadingVault": "Error al cargar bóveda: {e}", + "restoreUnhide": "Restaurar (Mostrar)", + "details": "Detalles", + "searchScrambledFiles": "Buscar archivos codificados...", + "permanentlyDeleteQuestion": "¿Estás seguro de que quieres eliminar permanentemente ", + "clearAll": "Limpiar Todo", + "backspace": "Retroceso", + "playbackSpeed": "Velocidad de Reproducción", + "lockControls": "Bloquear Controles", + "repeatMode": "Modo Repetición", + "copyUrlTooltip": "Copiar URL", + "mediaPathCopied": "Ruta del medio copiada al portapapeles.", + "volume": "Volumen", + "brightness": "Brillo", + "sortOptions": "Opciones de Orden", + "soundFX": "Efectos de Sonido", + "lyrics": "Letras", + "sleepTimer": "Temporizador de Sueño", + "playingQueue": "Cola de Reproducción ({count})", + "sleepTimerSet": "Temporizador de sueño configurado para {mins} minutos.", + "mins": "{m} Minutos", + "soundAndSpeedFX": "Efectos de Sonido y Velocidad", + "pitchAdjustment": "Ajuste de Tono", + "resetToDefault": "Restablecer a Predeterminado", + "backgroundPlaybackStopped": "Reproducción en segundo plano detenida", + "backgroundPlaybackEnabled": "Reproducción en segundo plano habilitada", + "viewSynchronizedLyrics": "Ver Letras Sincronizadas", + "soundFXAndEqualizer": "Efectos de Sonido y Ecualizador", + "setSleepTimer": "Configurar Temporizador", + "audioFileInfo": "Información del Archivo de Audio", + "lyricsLoaded": "Letras cargadas correctamente", + "loadLrcFile": "Cargar Archivo LRC", + "noDataToExport": "No hay datos para exportar.", + "exportedTo": "Exportado correctamente a {path}", + "exportFailed": "Exportación fallida: {e}", + "noTablesFound": "No se encontraron tablas en esta base de datos.", + "exportTableToCsv": "Exportar Tabla a CSV", + "searchRows": "Buscar filas...", + "noRowsFound": "No se encontraron filas", + "noSchemaLoaded": "No se cargaron detalles del esquema.", + "sqlEditor": "Editor SQL", + "selectTemplate": "Plantilla SELECT", + "enterSelectQuery": "Ingresar consulta SELECT aquí...", + "exportResultsToCsv": "Exportar Resultados a CSV", + "runQuery": "Ejecutar Consulta", + "typeLabel": "Tipo: {type}", + "defaultLabel": "Predeterminado: {val}", + "errorCreatingFolder": "Error al crear carpeta: {e}", + "createFolder": "Crear Carpeta", + "selectStorage": "Seleccionar Almacenamiento", + "clearSelection": "Limpiar Selección", + "pinSelected": "Fijar Seleccionado ({n})", + "pinThisFolder": "Fijar Esta Carpeta", + "addSelected": "Agregar Seleccionado ({n})", + "noPhysicalFilesToRename": "No se encontraron archivos físicos para renombrar", + "copedToClipboardWithName": "Copiado {name} al portapapeles", + "cutToClipboardWithName": "Cortado {name} al portapapeles", + "deletedItem": "Eliminado {name}", + "noItemsFound": "No se encontraron {type}", + "calculatingSizes": "Calculando tamaños...", + "contains": "Contiene", + "modified": "Modificado", + "permissions": "Permisos", + "itemsSelected": "Elementos Seleccionados", + "totalSize": "Tamaño Total", + "selectedPaths": "Rutas Seleccionadas:", + "ftpServerNotification": "Servidor FTP NFile", + "ftpRunningAt": "Ejecutándose en ftp://{ip}:{port}", + "ftpServerChannelName": "Servidor FTP", + "ftpServerChannelDesc": "Muestra el estado del Servidor FTP en segundo plano", + "nfileAudioPlayer": "Reproductor de Audio NFile", + "nfileArchiveOperations": "Operaciones de Archivo NFile", + "archiveProgressDesc": "Muestra el progreso de compresión y extracción de archivos", + "nfileStorage": "Almacenamiento NFile", + "internalStorageViaNFile": "Almacenamiento interno vía NFile", + "webSharingServer": "Servidor de Compartición Web", + "webSharingServerDesc": "Muestra el estado del Servidor de Compartición Web en segundo plano", + "nfileInternetWebShare": "NFile Compartición Web por Internet", + "nfileLocalWebShare": "NFile Compartición Web Local", + "runningAt": "Ejecutándose en {url}", + "nfileVersion": "NFile v1.0.43", + "storageAnalyzer": "Analizador de Almacenamiento", + "freeSpace": "Libre: {size}", + "totalSpace": "Total: {size}", + "type": "Tipo", + "movedItemsSuccessfully": "Elementos movidos correctamente", + "copiedItemsSuccessfully": "Elementos copiados correctamente", + "archiveCreatedSuccessfully": "¡Archivo \"{name}.{format}\" creado correctamente!", + "folderContains": "{f} subcarpeta(s), {d} archivo(s)", + "itemsSelectedCount": "{c} elementos ({f} carpeta(s), {d} archivo(s))", + "language": "Idioma", + "languageSub": "Seleccionar el idioma de la aplicación", + "systemDefault": "Predeterminado del sistema", + "spanish": "Español", + "english": "Inglés", + "threeDotDisabledInfoSub": "Elige qué mostrar al lado de los archivos y carpetas cuando los 3 puntos están ocultos", + "appExitBehaviorSub": "Elige entre pedir confirmación o presionar atrás dos veces para salir", + "noSettingsFound": "No se encontraron ajustes", + "trySearchingAnotherKeyword": "Intenta buscar con otra palabra clave", + "settingsCategories": "Categorías de Ajustes", + "showConfirmationDialog": "Mostrar diálogo de confirmación", + "vibrantOrange": "Naranja Vibrante", + "royalPurple": "Púrpura Real", + "emeraldGreen": "Verde Esmeralda", + "crimsonRed": "Rojo Carmesí", + "amberGold": "Dorado Ámbar", + "cyberpunkPink": "Rosa Cyberpunk", + "sapphireBlue": "Azul Zafiro", + "forestGreen": "Verde Bosque", + "sunsetPeach": "Melocotón Atardecer", + "defaultLogo": "Logo Predeterminado", + "outfitModernSans": "Outfit Modern Sans", + "jetBrainsTechMono": "JetBrains Tech Mono", + "montserratUrbanSans": "Montserrat Urban Sans", + "customImportedFont": "Fuente Importada Personalizada", + "signatureDefaultFont": "Original (Lexend Deca)", + "signatureDefaultFontDesc": "La estética geométrica original de NFile", + "outfitFontDesc": "Estética geométrica premium, mínima y elegante", + "jetBrainsFontDesc": "Estilo monoespaciado futurista para desarrolladores", + "montserratFontDesc": "Escala tipográfica llamativa, audaz y moderna", + "customFontTitle": "Fuente Personalizada ({name})", + "customFontDesc": "Tu archivo de fuente cargado a medida", + "replaceCustomFontFile": "Reemplazar archivo de fuente", + "importCustomFontFile": "Importar archivo de fuente (.ttf/.otf)", + "noneHideInfo": "Ninguna / Ocultar info", + "noneHideInfoDesc": "No mostrar información adicional a la derecha", + "dateTimeTitle": "Fecha y Hora", + "dateTimeDesc": "Muestra la fecha y hora de la última modificación", + "fileSizeItemCount": "Tamaño / Elementos", + "fileSizeItemCountDesc": "Muestra el tamaño para archivos y elementos para carpetas", + "confirmDialogTitle": "Diálogo de Confirmación", + "confirmDialogDesc": "Preguntar verificación antes de salir", + "doublePressToExit": "Doble pulsación para Salir", + "doublePressToExitDesc": "Toca atrás dos veces rápidamente para salir", + "neverManuallyClean": "Nunca (Limpiar manual)", + "days7": "7 Días", + "days15": "15 Días", + "days30Recommended": "30 Días (Recomendado)", + "trashDeletionWarning": "Los elementos se eliminarán permanentemente después de este tiempo.", + "fileExplorerAndNavigation": "Explorador de Archivos y Navegación", + "materialYouDynamic": "Material You (Colores Dinámicos del Fondo)", + "originalDefaultBlue": "Original Predeterminado (Azul Firma)", + "classicSolid": "Sólido Clásico (Material)", + "modernRounded": "Redondeado Moderno (Material)", + "starredSpecial": "Estrella Especial (Material)", + "snippetDocument": "Documento Recortado (Material)", + "minimalOutlined": "Contorno Mínimo (Material)", + "nfileBrokenOutline": "NFile Contorno Roto (Predeterminado)", + "categoryGridVuesax": "Cuadrícula de Categorías / Vuesax", + "chooseTrailingInfoDesc": "Elige qué mostrar al lado de los archivos y carpetas cuando los botones de 3 puntos están ocultos.", + "chooseAppLauncherIconDesc": "Elige un logo personalizado para el icono del lanzador. Algunos lanzadores pueden tardar unos segundos en actualizarse.", + "nothingDotMatrix": "Nothing Dot-Matrix & Sans", + "nothingDotMatrixDesc": "Encabezados retro de alta tecnología + cuerpo limpio", + "appTypographyTitle": "Tipografía de la App", + "selectTypefaceDesc": "Selecciona una tipografía hermosa para personalizar el tema visual general de NFile", + "doublePressBackToExit": "Presiona atrás dos veces para salir", + "mediaAndDefaultActions": "Medios y Acciones Predeterminadas", + "hamburgerClassicMenu": "Menú Hamburguesa / Clásico", + "dotMatrixSans": "Dot-Matrix & Sans", + "neverAutoDeleteDisabled": "Nunca (Eliminación automática deshabilitada)", + "after1Day": "Después de 1 Día", + "afterNDays": "Después de {days} Días", + "uiOperationCancelled": "Operación Cancelada", + "uiCompressionLimitExceeded": "Límite de Compresión Excedido", + "uiCompressingFiles": "Comprimiendo Archivos", + "uiExtractingArchive": "Extrayendo Archivo", + "uiExtremeSpeed": "Velocidad Extrema", + "uiStatelessCachingAsyncScans": "Caché sin estado y escaneos asíncronos", + "uiVaultSecure": "Bóveda Segura", + "uiEncryptedSafeWorkspace": "Espacio de trabajo seguro cifrado", + "uiServersHub": "Centro de Servidores", + "uiFtpLanSftpWebdav": "FTP, LAN, SFTP y WebDAV", + "uiRichUi": "Interfaz Rica", + "uiAmoledBlackBeautifulSeeds": "Negro AMOLED y hermosas semillas", + "uiDeleteFile": "Eliminar Archivo", + "uiNewFile": "Nuevo Archivo", + "uiBestForTextDocuments": "Ideal para documentos de texto", + "uiBestForBrochuresPhotos": "Ideal para folletos y fotos", + "uiPageLayout": "Diseño de Página", + "uiScrollDirection": "Dirección de Desplazamiento", + "uiDeletePermanentlyFromTheServer": "¿Eliminar \"${item.name}\" permanentemente del servidor?", + "uiDownloadsFileLocalClipboard": "Descarga archivo → portapapeles local", + "uiDownloadsAndDeletesFromServer": "Descarga y elimina del servidor", + "uiApplications": "Aplicaciones", + "uiImages": "Imágenes", + "uiVideos": "Vídeos", + "uiAudio": "Audio", + "uiDocuments": "Documentos", + "uiSystemOther": "Sistema / Otros", + "uiEgImage": "ej. Imagen_#", + "uiEg3": "ej. 3", + "uiEg1": "ej. 1", + "uiExistingFile": "Archivo Existente", + "uiDroppedFolder": "Carpeta Soltada", + "uiCurrentFolder": "Carpeta Actual", + "uiMoveHere": "Mover aquí", + "uiCutPasteItemIntoDestinationFolder": "Cortar y pegar elemento en la carpeta de destino", + "uiCopyHere": "Copiar aquí", + "uiLeavesOriginalFileIntactAndDuplicatesHere": "Deja el archivo original intacto y lo duplica aquí", + "uiCompressItemIntoAZiptarArchiveHere": "Comprime el elemento en un archivo zip/tar aquí", + "uiShowAllFilesAndFoldersInThisDirectory": "Mostrar todos los archivos y carpetas en este directorio", + "uiPdfsWordDocsSpreadsheetsTextsAndEbooks": "PDFs, docs de Word, hojas de cálculo, textos y e-books", + "uiJpegsPngsWebpsAndRawPhotoFormats": "JPEGs, PNGs, WebPs y formatos de foto raw", + "uiMp3sWavsAacsAndHighfidelityAudios": "MP3s, WAVs, AACs y audios de alta fidelidad", + "uiMp4sMkvsWebmsAndHighresVideoClips": "MP4s, MKVs, WebMs y clips de video de alta resolución", + "uiZips7zsRarsAndOtherCompressedAssets": "ZIPs, 7Zs, RARs y otros activos comprimidos", + "uiName": "Nombre", + "uiPath": "Ruta", + "uiSize": "Tamaño", + "deletePermanentlyFromServer": "¿Eliminar \"{name}\" permanentemente del servidor?", + "uiPreparingFoldersForSharing": "Preparando carpetas para compartir...", + "uiCompressingContentsPleaseWait": "Comprimiendo contenidos, por favor espere", + "uiCoreHighlights": "Características Principales", + "uiConnectShare": "Conectar y Compartir", + "uiNewlyCreatedOrDownloadedFilesWill": "Los archivos recién creados o descargados aparecerán aquí.", + "uiSqliteDatabaseReader": "Lector de Base de Datos SQLite", + "uiFailedToOpenDatabase": "Fallo al abrir base de datos", + "uiPk": "PK", + "uiNotNull": "NO NULO", + "uiCreateANewDirectory": "Crear un nuevo directorio", + "uiCreateANewEmptyTextDocument": "Crear un nuevo documento de texto vacío", + "uiNewArchive": "Nuevo Archivo", + "uiCompressCurrentFolderContents": "Comprimir el contenido de la carpeta actual", + "uiLayoutMode": "Modo de Diseño", + "uiListView": "Vista de Lista", + "uiGridView": "Vista de Cuadrícula", + "uiSizePaddingOptions": "Opciones de Tamaño y Espaciado", + "uiIconFolderSize": "Tamaño de Icono y Carpeta", + "uiItemPaddingSpacing": "Espaciado y Relleno de Elementos", + "uiSortBy": "Ordenar Por", + "uiOnlyThisFolder": "Solo esta carpeta", + "uiEnableCustomSortingSpecificToThis": "Habilitar orden personalizado específico para esta carpeta", + "uiStorageVolumes": "Volúmenes de Almacenamiento", + "uiNetworkConnections": "Conexiones de Red", + "uiNoResultsFound": "No se encontraron resultados", + "uiEmptyFolder": "Carpeta Vacía", + "uiThisDirectoryDoesNotContainAny": "Este directorio no contiene archivos ni subcarpetas.", + "uiPastedHoldingClipboardForMultiplePastes": "Pegado (manteniendo en portapapeles para múltiples pegados)", + "uiFiles": "Archivos", + "uiPdfDisplaySettings": "Configuración de Vista PDF", + "uiOptimizeRenderingPerformanceForLargeDesignheavy": "Optimizar rendimiento para documentos grandes, con diseño o escaneados.", + "uiQuickPerformancePresets": "Ajustes Rápidos de Rendimiento", + "uiDetailedTuningOptions": "Opciones de Ajuste Detalladas", + "uiDisableToSignificantlyBoostPageRendering": "Desactívalo para aumentar la velocidad y eliminar saltos al desplazar.", + "uiNetworkStatus": "Estado de red", + "uiConnected": "Conectado", + "uiServerAddress": "Dirección del servidor", + "uiFailedToLoadImage": "Fallo al cargar la imagen", + "uiSelectStorageDrive": "Seleccionar Unidad de Almacenamiento", + "uiLongPressToOpenWith": "Mantén pulsado para Abrir con...", + "uiAllItems": "Todos los Elementos", + "uiFolders": "Carpetas", + "uiRemoteConnections": "Conexiones Remotas", + "uiSelectNetworkService": "Seleccionar Servicio de Red", + "uiMountARemoteServerOrNas": "Monta un servidor remoto o NAS como unidad dinámica.", + "uiEnterConnectionDetailsToLinkThis": "Introduce los detalles para enlazar este volumen de red.", + "uiCreatingMountPoint": "Creando Punto de Montaje...", + "uiRecycleBinIsEmpty": "La Papelera está Vacía", + "uiItemsYouDeleteWhenRecycleBin": "Los elementos eliminados con la Papelera activada aparecerán aquí.", + "uiNewRemoteFolder": "Nueva Carpeta Remota", + "uiConnectionLost": "Conexión Perdida", + "uiEmptyDirectory": "Directorio Vacío", + "uiChooseProtectionMode": "Elegir Modo de Protección", + "uiChooseHowYouWantToProtect": "Elige cómo quieres proteger tus archivos (cifrado XOR).", + "uiActive": "Activo", + "uiHideFiles": "Ocultar Archivos", + "uiSecurityStorage": "ALMACENAMIENTO SEGURO", + "uiTotalSpaceSecured": "Espacio Total Asegurado", + "uiHiddenFiles": "Archivos Ocultos", + "uiEstablishingSecureProxyRelay": "Estableciendo retransmisión segura...", + "uiScanQrCode": "Escanear Código QR", + "uiWebSharingHub": "Centro de Intercambio Web", + "uiLocalWebShare": "Compartir Web Local", + "uiInternetShareLink": "Enlace de Internet", + "uiHttpLocalShareServer": "Servidor HTTP Local", + "uiAllowsOtherDevicesOnTheSame": "Permite a otros dispositivos en la misma Wi-Fi acceder a tus archivos.", + "uiServerOnlineStreaming": "Servidor en Línea", + "uiDirectBrowserUrl": "URL Directa del Navegador:", + "uiServerIsIdle": "El Servidor está Inactivo", + "uiMakeSureOtherDevicesAreOn": "Asegúrate de que otros dispositivos estén en la misma Wi-Fi e inicia el servidor.", + "uiInternetShareTunnel": "Túnel de Internet", + "uiGeneratesASecureTemporaryPublicTunnel": "Genera un túnel público seguro temporal. Comparte este enlace con cualquier persona.", + "uiCloudTunnelActive": "Túnel en la Nube Activo", + "uiTemporaryShareLinkActive24h": "Enlace Temporal Compartido (Activo 24h):", + "uiConnectedBrowserClients": "Clientes Conectados", + "uiWaitingForIncomingInternetDownloads": "Esperando descargas de internet...", + "uiInternetSharingInactive": "Compartir por Internet Inactivo", + "uiActivateTheTunnelToEstablishA": "Activa el túnel para establecer un enlace seguro a través de Internet.", + "uiLosslessAudio": "Audio sin Pérdida", + "uiNoSynchronizedLyricsFound": "No se encontraron letras sincronizadas", + "uiKeepALrcFileWithThe": "Guarda un archivo .lrc con el mismo nombre o selecciónalo manualmente.", + "uiTapALineToSeekPlayback": "Toca una línea para buscar en la reproducción", + "uiAppManager": "Gestor de Aplicaciones", + "uiExactStorageCalculation": "Cálculo Exacto de Almacenamiento", + "uiToSeeExactAppStorageSizes": "Para ver el tamaño exacto, habilita el permiso de Acceso de Uso en los Ajustes del Sistema.", + "uiGrantUsageAccessPermission": "Otorgar Permiso", + "uiStorageAnalytics": "Análisis de Almacenamiento", + "uiScanningDeviceStorage": "Escaneando Almacenamiento del Dispositivo", + "uiAnalyzingFilesCategorizingAssetsAndReading": "Analizando archivos, categorizando e inspeccionando espacio de apps...", + "uiTotalStorage": "Almacenamiento Total", + "uiBreakdown": "Desglose", + "uiNoApplicationsFound": "No se encontraron aplicaciones", + "uiNoBackupsFound": "No se encontraron backups", + "uiSlideTapToUnlock": "Desliza / Toca para Desbloquear", + "uiHwDec": "Dec HW", + "uiOverallProgress": "Progreso General", + "uiRenamingFiles": "Renombrando archivos...", + "uiPleaseWaitUpdatingFolderContent": "Por favor espera, actualizando contenido", + "uiBatchRename": "Renombrado por Lotes", + "uiRenamePreview": "Vista Previa", + "uiBackToEdit": "Volver", + "uiApplyChanges": "Aplicar Cambios", + "uiFileAlreadyExists": "El Archivo ya Existe", + "uiApplyToAllRemainingConflicts": "Aplicar a conflictos restantes", + "uiNewer": "Más Reciente", + "uiDragDropOptions": "Opciones de Arrastrar y Soltar", + "uiDestinationLocation": "Ubicación de Destino", + "uiChooseAction": "Elegir Acción", + "uiExtractArchive": "Extraer Archivo", + "uiFilterFilesByType": "Filtrar Archivos", + "uiSelectACategoryToDisplayMatching": "Selecciona una categoría para ver solo esos archivos", + "uiNoMatchingDirectoriesOrFilesFound": "No se encontraron resultados", + "uiBuiltinNfileViewer": "Visor Integrado", + "uiSystemExternalApp": "App Externa", + "uiOpenWithThirdPartyAppsOn": "Abrir con aplicaciones de terceros", + "uiSearchInTab": "Buscar en la pestaña", + "uiInternalStorage": "Almacenamiento Interno", + "uiBrowseDeviceFiles": "Explorar archivos del dispositivo", + "uiCustomize": "Personalizar", + "uiNoShortcutsPinnedTapCustomizeTo": "No hay atajos anclados. Toca Personalizar.", + "uiDragItemsByTheHandleTo": "Arrastra los elementos por el control (=) para reordenarlos.", + "uiDefaultScanLocations": "Ubicaciones de Escaneo por Defecto:", + "uiCustomScanLocations": "Ubicaciones de Escaneo Personalizadas:", + "uiNoCustomPathsAdded": "No hay rutas personalizadas.", + "uiRecentFiles": "Archivos Recientes", + "uiViewAll": "Ver Todo", + "uiRestrictedSystemFolder": "Carpeta de Sistema Restringida", + "uiAndroid11RestrictsStandardAccessTo": "Android 11+ restringe el acceso a Android/data y obb. NFile requiere permisos avanzados.", + "errorShizukuNotRunning": "Acceso denegado. Shizuku no está en ejecución o no está autorizado.", + "vaultEnterPin": "Ingresa el PIN para Desbloquear", + "vaultSetPin": "Establece tu PIN de 4 dígitos", + "vaultConfirmPin": "Confirma tu PIN de 4 dígitos", + "vaultPinSuccess": "¡PIN guardado correctamente!", + "vaultPinMismatch": "Los PIN no coinciden. ¡Inténtalo de nuevo!", + "vaultPinIncorrect": "PIN incorrecto. ¡Inténtalo de nuevo!", + "actionStop": "Detener", + "actionStart": "Iniciar", + "actionAnonymous": "Anónimo", + "sharingDirectory": "Directorio Compartido: {dir}", + "stopWebServer": "Detener Servidor Web", + "startWebServer": "Iniciar Servidor Web", + "protocolDescSmb": "Red de Área Local y NAS SMB", + "protocolDescFtp": "Protocolo de Transferencia de Archivos Estándar", + "protocolDescSftp": "Servidor de Transferencia Segura por SSH", + "protocolDescWebDav": "Servidor HTTP de Autoría Distribuida", + "protocolDescSaf": "Acceso a Almacenamiento de Android (Tarjeta SD / Externo)", + "sortNameAsc": "Nombre (A-Z)", + "sortNameDesc": "Nombre (Z-A)", + "sortNewest": "Más nuevo", + "sortOldest": "Más antiguo", + "sortSizeLarge": "Tamaño (Mayor)", + "sortSizeSmall": "Tamaño (Menor)", + "sortType": "Tipo", + "webSharingDirectory": "Directorio Compartido: {dir}", + "aboutCopyright": "Copyright © 2026 NFile. Todos los derechos reservados.", + "aboutDescription": "NFile es un hermoso gestor de archivos de código abierto y centro multimedia offline construido con Flutter. Diseñado para rendimiento extremo, estética glasomórfica y experiencias de usuario fluidas.", + "aboutMadeWith": "Hecho con ❤️ por Rubex", + "aboutVersion": "v1.0.42 (Estable)", + "stepXofY": "Paso {step} de {total}", + "connectingMessage": "Por favor espera mientras establecemos una conexión con el servidor {type}.", + "documentsUiWarning": "Tu dispositivo no tiene la app predeterminada de Archivos del Sistema (DocumentsUI) habilitada, ", + "deletedLabel": "Eliminado: {date} • {size}", + "originalPathLabel": "Ruta Original: {path}", + "configuringItems": "Configurando {count} elementos", + "reviewingItems": "Revisando {count} elementos", + "copiedItems": "{count} elemento(s) copiado(s)", + "cutItems": "{count} elemento(s) cortado(s)", + "protectedItems": "{count} elementos protegidos exitosamente.", + "noMatchesFound": "No pudimos encontrar nada que coincida con ", + "filesCount": "archivos: {count}", + "foldersCount": "carpetas: {count}", + "queryReturned": "La consulta devolvió {count} filas", + "showingRows": "Mostrando {start} - {end}", + "processingItem": "Procesando elemento {current} de {total}", + "compressionLevel": "Nivel de Compresión: {label}", + "videoCodecInfo": "AVC / AAC • 1080p", + "scanQrMessage": "Escanea con otro dispositivo para abrir {type} inmediatamente.", + "conflictFileExists": "Ya existe un archivo llamado ", + "appSizeInfo": "Tamaño: {size} • Instalado: {date}", + "backupSizeInfo": "Tamaño: {size}", + "webSearchPlaceholder": "Buscar archivos y carpetas...", + "webUploadBtn": "Subir", + "webUploadTooltip": "Subir Archivos a esta Carpeta", + "webParentDir": ".. (Carpeta Superior)", + "webGoUpLevel": "Subir un nivel", + "webNoResults": "No se encontraron resultados", + "webCheckSpelling": "Revisá la ortografía o probá otro término de búsqueda.", + "webFileName": "Nombre de Archivo", + "webCopyLink": "Copiar Enlace", + "webDownload": "Descargar", + "webCloseModal": "Cerrar", + "webDownloadFile": "Descargar Archivo", + "webDropFiles": "Soltá los archivos aquí para subirlos", + "webUploadInstantly": "Tus archivos se subirán instantáneamente a esta carpeta", + "webUploadingFile": "Subiendo archivo...", + "webSecurelySharing": "Compartiendo archivos de forma segura vía NFile", + "webUploadSuccess": "¡Subida completada exitosamente!", + "webLinkCopied": "¡Enlace copiado al portapapeles!", + "webLinkCopyFailed": "Error al copiar el enlace.", + "webUploadingName": "Subiendo \\${file.name}...", + "webUploadFailedName": "Error al subir \\${file.name}", + "webLoadingPreview": "Cargando vista previa...", + "webStreamFailed": "Error al cargar el documento. Podés descargarlo directamente.", + "webPreviewNotSupported": "Vista previa no disponible para este tipo de archivo", + "webClickDownload": "Hacé clic en Descargar para guardarlo en tu dispositivo.", + "webVideoNotSupported": "Tu navegador no soporta la reproducción de video.", + "webAudioNotSupported": "Tu navegador no soporta la reproducción de audio.", + "webUploadFailed": "Error en la subida", + "webNetworkError": "Error de red" +} \ No newline at end of file diff --git a/lib/core/app_strings.dart b/lib/core/app_strings.dart new file mode 100644 index 0000000..43692ca --- /dev/null +++ b/lib/core/app_strings.dart @@ -0,0 +1,943 @@ +import 'dart:convert'; +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; +import '../services/preferences_service.dart'; + +class AppStrings { + static AppStrings _instance = AppStrings._(); + static AppStrings get current => _instance; + static String _localeSetting = 'system'; + static Map _localizedStrings = {}; + + static String get _locale { + if (_localeSetting == 'system') { + try { + final platformLocale = WidgetsBinding.instance.platformDispatcher.locale.languageCode; + return (platformLocale == 'es') ? 'es' : 'en'; + } catch (_) { + return 'en'; // fallback if called before bindings are ready + } + } + return _localeSetting; + } + + static String get locale => _locale; + + static set locale(String value) { + _localeSetting = value; + _instance = AppStrings._(); + } + + static Future setLocale(BuildContext context, String newLocale) async { + PreferencesService.saveLocale(newLocale); + _localeSetting = newLocale; + await loadTranslations(); + _instance = AppStrings._(); + final router = context.findAncestorStateOfType(); + if (router != null) { + // ignore: invalid_use_of_protected_member + (router as dynamic).setState?.call(() {}); + } + } + + static String getLocale() => _locale; + + AppStrings._(); + + factory AppStrings() => _instance; + + static const List supportedLocales = [ + Locale('en'), + Locale('es'), + ]; + + static const LocalizationsDelegate delegate = _AppStringsDelegate(); + + static Future loadTranslations() async { + try { + final jsonString = await rootBundle.loadString('assets/i18n/$_locale.json'); + _localizedStrings = json.decode(jsonString); + } catch (e) { + _localizedStrings = {}; + } + } + + String get cancel => _localizedStrings['cancel'] ?? 'Cancel'; + String get ok => _localizedStrings['ok'] ?? 'OK'; + String get save => _localizedStrings['save'] ?? 'Save'; + String get delete => _localizedStrings['delete'] ?? 'Eliminar'; + String get rename => _localizedStrings['rename'] ?? 'Renombrar'; + String get copy => _localizedStrings['copy'] ?? 'Copiar'; + String get cut => _localizedStrings['cut'] ?? 'Cortar'; + String get paste => _localizedStrings['paste'] ?? 'Paste'; + String get share => _localizedStrings['share'] ?? 'Compartir'; + String get extract => _localizedStrings['extract'] ?? 'Extract'; + String get archive => _localizedStrings['archive'] ?? 'Archive'; + String get close => _localizedStrings['close'] ?? 'Close'; + String get done => _localizedStrings['done'] ?? 'Done'; + String get back => _localizedStrings['back'] ?? 'Back'; + String get exit => _localizedStrings['exit'] ?? 'Exit'; + String get home => _localizedStrings['home'] ?? 'Home'; + String get browse => _localizedStrings['browse'] ?? 'Browse'; + String get search => _localizedStrings['search'] ?? 'Search'; + String get selectAll => _localizedStrings['selectAll'] ?? 'Select All'; + String get refresh => _localizedStrings['refresh'] ?? 'Refresh'; + String get properties => _localizedStrings['properties'] ?? 'Propiedades'; + String get info => _localizedStrings['info'] ?? 'Info'; + String get more => _localizedStrings['more'] ?? 'More'; + String get preview => _localizedStrings['preview'] ?? 'Preview'; + String get create => _localizedStrings['create'] ?? 'Create'; + String get open => _localizedStrings['open'] ?? 'Open'; + String get edit => _localizedStrings['edit'] ?? 'Edit'; + String get upload => _localizedStrings['upload'] ?? 'Upload'; + String get download => _localizedStrings['download'] ?? 'Download'; + String get connect => _localizedStrings['connect'] ?? 'Connect'; + String get disconnect => _localizedStrings['disconnect'] ?? 'Disconnect'; + String get restore => _localizedStrings['restore'] ?? 'Restore'; + String get clear => _localizedStrings['clear'] ?? 'Clear'; + String get skip => _localizedStrings['skip'] ?? 'Skip'; + String get replace => _localizedStrings['replace'] ?? 'Replace'; + String get keepBoth => _localizedStrings['keepBoth'] ?? 'Keep Both'; + String get uninstall => _localizedStrings['uninstall'] ?? 'Uninstall'; + String get backup => _localizedStrings['backup'] ?? 'Backup'; + String get confirm => _localizedStrings['confirm'] ?? 'Confirm'; + String get appTitle => _localizedStrings['appTitle'] ?? 'NFile'; + String get appSubtitle => _localizedStrings['appSubtitle'] ?? 'Beautiful Media Suite'; + String get grantPermission => _localizedStrings['grantPermission'] ?? 'Grant Permission'; + String get storageAccessRequired => _localizedStrings['storageAccessRequired'] ?? 'Storage Access Required'; + String get storagePermissionMessage => _localizedStrings['storagePermissionMessage'] ?? 'NFile requires storage permission to manage, organize, and display your media files seamlessly.'; + String get openingSharedDocument => _localizedStrings['openingSharedDocument'] ?? 'Opening shared document...'; + String get resolvingSecureContent => _localizedStrings['resolvingSecureContent'] ?? 'Resolving secure content stream'; + String get myFiles => _localizedStrings['myFiles'] ?? 'My Files'; + String get refreshDashboard => _localizedStrings['refreshDashboard'] ?? 'Refresh Dashboard'; + String get dashboardRefreshed => _localizedStrings['dashboardRefreshed'] ?? 'Dashboard refreshed successfully'; + String get exitApplication => _localizedStrings['exitApplication'] ?? 'Exit Application'; + String get exitConfirmation => _localizedStrings['exitConfirmation'] ?? 'Exit Confirmation'; + String get exitConfirmationMessage => _localizedStrings['exitConfirmationMessage'] ?? 'Are you sure you want to exit? Press back again or tap Exit to close the app.'; + String get pressBackAgain => _localizedStrings['pressBackAgain'] ?? 'Press back again to exit'; + String get confirmDeletion => _localizedStrings['confirmDeletion'] ?? 'Confirm Deletion'; + String get deletePermanently => _localizedStrings['deletePermanently'] ?? 'Delete Permanently'; + String get deletePermanentlyQuestion => _localizedStrings['deletePermanentlyQuestion'] ?? 'Delete Permanently?'; + String get deleteSelectedItems => _localizedStrings['deleteSelectedItems'] ?? 'Delete Selected Items'; + String get deleteSourceFiles => _localizedStrings['deleteSourceFiles'] ?? 'Delete source files after completion'; + String permanentlyDeleteItems(int count) => (_localizedStrings['permanentlyDeleteItems'] ?? 'Are you sure you want to permanently delete {count} selected items?').replaceAll('{count}', count.toString()); + String permanentlyDeleteItemsArchive(int count) => (_localizedStrings['permanentlyDeleteItemsArchive'] ?? 'Are you sure you want to delete precisely these {count} item(s) from the archive? This cannot be undone.').replaceAll('{count}', count.toString()); + String deletedSuccessfully(String count) => (_localizedStrings['deletedSuccessfully'] ?? 'Successfully deleted {count}').replaceAll('{count}', count.toString()); + String permanentlyDeleted(int count) => (_localizedStrings['permanentlyDeleted'] ?? 'Permanently deleted {count} item(s)').replaceAll('{count}', count.toString()); + String get failedToDelete => _localizedStrings['failedToDelete'] ?? 'Failed to delete items'; + String errorDeleting(String e) => (_localizedStrings['errorDeleting'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get itemsDeleted => _localizedStrings['itemsDeleted'] ?? 'Items deleted successfully'; + String get recycleBin => _localizedStrings['recycleBin'] ?? 'Recycle Bin'; + String get emptyRecycleBin => _localizedStrings['emptyRecycleBin'] ?? 'Empty Recycle Bin'; + String get emptyRecycleBinQuestion => _localizedStrings['emptyRecycleBinQuestion'] ?? 'Empty Recycle Bin?'; + String get emptyBin => _localizedStrings['emptyBin'] ?? 'Empty Bin'; + String get recycleBinEmptied => _localizedStrings['recycleBinEmptied'] ?? 'Recycle Bin emptied successfully'; + String errorEmptyingBin(String e) => (_localizedStrings['errorEmptyingBin'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get emptyRecycleBinMessage => _localizedStrings['emptyRecycleBinMessage'] ?? 'Are you sure you want to permanently delete all items in the Recycle Bin? This action is irreversible.'; + String deletePermanentlyRecycleMessage(int count) => (_localizedStrings['deletePermanentlyRecycleMessage'] ?? 'Are you sure you want to permanently delete these {count} item(s)? This action cannot be undone.').replaceAll('{count}', count.toString()); + String select(int n) => (_localizedStrings['select'] ?? '{n} Selected').replaceAll('{n}', n.toString()); + String get searchDeletedFiles => _localizedStrings['searchDeletedFiles'] ?? 'Search deleted files...'; + String restoredItems(int count) => (_localizedStrings['restoredItems'] ?? 'Restored {count} item(s) successfully').replaceAll('{count}', count.toString()); + String errorRestoring(String e) => (_localizedStrings['errorRestoring'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get moreSettings => _localizedStrings['moreSettings'] ?? 'More Settings'; + String get searchSettings => _localizedStrings['searchSettings'] ?? 'Search settings...'; + String get generalAndBehavior => _localizedStrings['generalAndBehavior'] ?? 'General & Behavior'; + String get generalAndBehaviorSub => _localizedStrings['generalAndBehaviorSub'] ?? 'Default screen, navigation controls, and shortcuts'; + String get appearanceAndThemes => _localizedStrings['appearanceAndThemes'] ?? 'Appearance & Themes'; + String get appearanceAndThemesSub => _localizedStrings['appearanceAndThemesSub'] ?? 'Themes, app icons, folder styles, and typography'; + String get fileExplorerOptions => _localizedStrings['fileExplorerOptions'] ?? 'File Explorer Options'; + String get fileExplorerOptionsSub => _localizedStrings['fileExplorerOptionsSub'] ?? 'Address bar, hidden files, tabs, and drag & drop'; + String get listAndLayout => _localizedStrings['listAndLayout'] ?? 'List & Layout Styling'; + String get listAndLayoutSub => _localizedStrings['listAndLayoutSub'] ?? 'Folder sizes, counts, and time/date formats'; + String get mediaPreferences => _localizedStrings['mediaPreferences'] ?? 'Media Preferences'; + String get mediaPreferencesSub => _localizedStrings['mediaPreferencesSub'] ?? 'Default album view and thumbnail previews'; + String get fileActionsAndViewers => _localizedStrings['fileActionsAndViewers'] ?? 'File Actions & Viewers'; + String get fileActionsAndViewersSub => _localizedStrings['fileActionsAndViewersSub'] ?? 'Open actions and default viewers configuration'; + String get recycleBinTrash => _localizedStrings['recycleBinTrash'] ?? 'Recycle Bin (Trash)'; + String get recycleBinTrashSub => _localizedStrings['recycleBinTrashSub'] ?? 'Recycle bin toggles and auto-delete duration'; + String get backupAndRestore => _localizedStrings['backupAndRestore'] ?? 'Backup & Restore'; + String get backupAndRestoreSub => _localizedStrings['backupAndRestoreSub'] ?? 'Backup your settings to a JSON file or restore them'; + String get defaultToBrowseScreen => _localizedStrings['defaultToBrowseScreen'] ?? 'Default to Browse Screen'; + String get defaultToBrowseScreenSub => _localizedStrings['defaultToBrowseScreenSub'] ?? 'Directly launch into the Browse storage explorer on app start'; + String get rememberLastFolder => _localizedStrings['rememberLastFolder'] ?? 'Remember Last Opened Folder'; + String get rememberLastFolderSub => _localizedStrings['rememberLastFolderSub'] ?? 'Open the last folder you browsed when launching the app'; + String get showHomeBrowseBar => _localizedStrings['showHomeBrowseBar'] ?? 'Show Home & Browse Bottom Bar'; + String get showHomeBrowseBarSub => _localizedStrings['showHomeBrowseBarSub'] ?? 'Toggle bottom navigation bar visibility on the Home screen'; + String get hideNavLabels => _localizedStrings['hideNavLabels'] ?? 'Hide Bottom Navigation Labels'; + String get hideNavLabelsSub => _localizedStrings['hideNavLabelsSub'] ?? 'Hide text labels of the bottom bar (Home/Browse) for a cleaner and compact look'; + String get hideAndroidNavBar => _localizedStrings['hideAndroidNavBar'] ?? 'Hide Android Navigation Bar'; + String get hideAndroidNavBarSub => _localizedStrings['hideAndroidNavBarSub'] ?? 'Hide bottom navigation bar to maximize screen real estate (swiping up displays it)'; + String get showBottomNavBar => _localizedStrings['showBottomNavBar'] ?? 'Show Bottom Navigation Bar'; + String get showBottomNavBarSub => _localizedStrings['showBottomNavBarSub'] ?? 'Enable bottom action bar on Browse screen'; + String get hideActionBarLabels => _localizedStrings['hideActionBarLabels'] ?? 'Hide Action Bar Text Labels'; + String get hideActionBarLabelsSub => _localizedStrings['hideActionBarLabelsSub'] ?? 'Show only icons in selection action bar at bottom of Browse & Media screens'; + String get customizeShortcuts => _localizedStrings['customizeShortcuts'] ?? 'Customize Shortcuts'; + String get customizeShortcutsSub => _localizedStrings['customizeShortcutsSub'] ?? 'Reorder and toggle visibility of quick category items'; + String get showRecentFiles => _localizedStrings['showRecentFiles'] ?? 'Show Recent Files'; + String get showRecentFilesSub => _localizedStrings['showRecentFilesSub'] ?? 'Display the list of recently accessed files on the Home screen'; + String get preventLeftBackGesture => _localizedStrings['preventLeftBackGesture'] ?? 'Prevent Left Back Gesture for Drawer'; + String get preventLeftBackGestureSub => _localizedStrings['preventLeftBackGestureSub'] ?? 'Excludes the left edge of the screen from Android system back gestures, making it easier to swipe open the drawer. You can still swipe from the right edge to go back.'; + String get appExitBehavior => _localizedStrings['appExitBehavior'] ?? 'App Exit Behavior'; + String get accentColorTheme => _localizedStrings['accentColorTheme'] ?? 'Accent Color / Dynamic Theme'; + String get folderIconStyle => _localizedStrings['folderIconStyle'] ?? 'Folder Icon Style'; + String get appDrawerButtonStyle => _localizedStrings['appDrawerButtonStyle'] ?? 'App Drawer Button Style'; + String get amoledBlackMode => _localizedStrings['amoledBlackMode'] ?? 'AMOLED Black Mode'; + String get amoledBlackModeSub => _localizedStrings['amoledBlackModeSub'] ?? 'Use pitch black background in Dark Mode for AMOLED screens'; + String get appIcon => _localizedStrings['appIcon'] ?? 'App Icon'; + String get appTypography => _localizedStrings['appTypography'] ?? 'App Typography / Font Family'; + String get useMaterialIcons => _localizedStrings['useMaterialIcons'] ?? 'Use Expressive Material Icons'; + String get useMaterialIconsSub => _localizedStrings['useMaterialIconsSub'] ?? 'Replace custom Broken icons with standard Material Design icons'; + String get showAddressBar => _localizedStrings['showAddressBar'] ?? 'Show Address Bar'; + String get showAddressBarSub => _localizedStrings['showAddressBarSub'] ?? 'Display an editable Windows-Explorer-style address bar at the top of file list'; + String get showFloatingButton => _localizedStrings['showFloatingButton'] ?? 'Show Floating \'+\' Button'; + String get showFloatingButtonSub => _localizedStrings['showFloatingButtonSub'] ?? 'Enable quick creation (+) button at bottom of Browse screen'; + String get showHiddenFiles => _localizedStrings['showHiddenFiles'] ?? 'Show Hidden Files'; + String get showHiddenFilesSub => _localizedStrings['showHiddenFilesSub'] ?? 'Display system files and folders starting with a dot (.)'; + String get highlightExitedFolder => _localizedStrings['highlightExitedFolder'] ?? 'Highlight Exited Folder'; + String get highlightExitedFolderSub => _localizedStrings['highlightExitedFolderSub'] ?? 'Briefly flash and scroll to the folder you just exited when going back'; + String get enableMultipleTabs => _localizedStrings['enableMultipleTabs'] ?? 'Enable Multiple Tabs'; + String get enableMultipleTabsSub => _localizedStrings['enableMultipleTabsSub'] ?? 'Allow opening multiple folders in separate tabs for quick navigation'; + String get enableSplitScreen => _localizedStrings['enableSplitScreen'] ?? 'Enable Split Screen'; + String get enableSplitScreenSub => _localizedStrings['enableSplitScreenSub'] ?? 'Browse two directories side by side and transfer files easily'; + String get enableDragAndDrop => _localizedStrings['enableDragAndDrop'] ?? 'Enable Drag & Drop'; + String get enableDragAndDropSub => _localizedStrings['enableDragAndDropSub'] ?? 'Long press and drag folders or files to move them into other folders'; + String get confirmDragDrop => _localizedStrings['confirmDragDrop'] ?? 'Confirm Drag & Drop Actions'; + String get confirmDragDropSub => _localizedStrings['confirmDragDropSub'] ?? 'Show options popup (Copy, Move, Archive) when dropping files'; + String get showFolderFileCount => _localizedStrings['showFolderFileCount'] ?? 'Show Folder & File Count Header'; + String get showFolderFileCountSub => _localizedStrings['showFolderFileCountSub'] ?? 'Display total folders and files count under storage title bar'; + String get showFolderContentCount => _localizedStrings['showFolderContentCount'] ?? 'Show Folder Content Count'; + String get showFolderContentCountSub => _localizedStrings['showFolderContentCountSub'] ?? 'Calculate and display total files and folders inside directory listings'; + String get showFolderSize => _localizedStrings['showFolderSize'] ?? 'Show Folder Size'; + String get showFolderSizeSub => _localizedStrings['showFolderSizeSub'] ?? 'Calculate and display total size of all files inside directories (can affect listing performance)'; + String get use24HourFormat => _localizedStrings['use24HourFormat'] ?? 'Use 24-Hour Time Format'; + String get use24HourFormatSub => _localizedStrings['use24HourFormatSub'] ?? 'Toggle between 12-hour (AM/PM) and 24-hour time formatting across lists'; + String get hideTimeDate => _localizedStrings['hideTimeDate'] ?? 'Hide Time & Date from Lists'; + String get hideTimeDateSub => _localizedStrings['hideTimeDateSub'] ?? 'Completely hide modification dates and times under files and folders'; + String get adaptiveMultiLine => _localizedStrings['adaptiveMultiLine'] ?? 'Adaptive Multi-line Filenames'; + String get adaptiveMultiLineSub => _localizedStrings['adaptiveMultiLineSub'] ?? 'Allow filenames to wrap 3 lines instead of truncating'; + String get hide3DotButtons => _localizedStrings['hide3DotButtons'] ?? 'Hide 3-Dot Action Buttons'; + String get hide3DotButtonsSub => _localizedStrings['hide3DotButtonsSub'] ?? 'Hide the three-dot option menu button next to folders and files'; + String get threeDotDisabledInfo => _localizedStrings['threeDotDisabledInfo'] ?? '3-Dot Disabled Trailing Info'; + String get defaultAlbumView => _localizedStrings['defaultAlbumView'] ?? 'Default Album Preferred View'; + String get defaultAlbumViewSub => _localizedStrings['defaultAlbumViewSub'] ?? 'Open Images/Videos quick categories directly in Folders (Albums) preferred view'; + String get showMediaPreviews => _localizedStrings['showMediaPreviews'] ?? 'Show Media Previews'; + String get showMediaPreviewsSub => _localizedStrings['showMediaPreviewsSub'] ?? 'Display actual image and video thumbnails instead of generic file icons'; + String get skipOpenWithDialog => _localizedStrings['skipOpenWithDialog'] ?? 'Skip "Open With" Dialog'; + String get skipOpenWithDialogSub => _localizedStrings['skipOpenWithDialogSub'] ?? 'Bypass the application choice dialog and immediately open files with default viewers'; + String get resetDefaultViewers => _localizedStrings['resetDefaultViewers'] ?? 'Reset Default File Viewers'; + String get resetDefaultViewersSub => _localizedStrings['resetDefaultViewersSub'] ?? 'Clear all remembered "Open With" associations for file viewers'; + String get viewerChoicesReset => _localizedStrings['viewerChoicesReset'] ?? 'All default viewer choices have been reset'; + String get enableRecycleBin => _localizedStrings['enableRecycleBin'] ?? 'Enable Recycle Bin'; + String get enableRecycleBinSub => _localizedStrings['enableRecycleBinSub'] ?? 'Move deleted files and folders to a hidden Recycle Bin instead of deleting permanently'; + String get autoDeleteTrashDuration => _localizedStrings['autoDeleteTrashDuration'] ?? 'Auto-Delete Trash Duration'; + String get backupSettings => _localizedStrings['backupSettings'] ?? 'Config. de Copia de Seguridad'; + String get backupSettingsSub => _localizedStrings['backupSettingsSub'] ?? 'Guardar toda tu configuración actual en NFile/Backups/Settings/'; + String get restoreSettings => _localizedStrings['restoreSettings'] ?? 'Restaurar Configuración'; + String get restoreSettingsSub => _localizedStrings['restoreSettingsSub'] ?? 'Seleccionar y restaurar configuración desde un archivo backup JSON'; + String get settingsBackedUp => _localizedStrings['settingsBackedUp'] ?? 'Settings backed up to NFile/Backups/Settings/nfile_settings_backup.json'; + String get settingsRestored => _localizedStrings['settingsRestored'] ?? 'Settings restored successfully!'; + String failedToBackup(String e) => (_localizedStrings['failedToBackup'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String failedToRestore(String e) => (_localizedStrings['failedToRestore'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get chooseTrailingInfoStyle => _localizedStrings['chooseTrailingInfoStyle'] ?? 'Choose Trailing Info Style'; + String get chooseExitBehavior => _localizedStrings['chooseExitBehavior'] ?? 'Choose Exit Behavior'; + String get chooseAccentTheme => _localizedStrings['chooseAccentTheme'] ?? 'Choose Accent Theme'; + String get chooseFolderIconStyle => _localizedStrings['chooseFolderIconStyle'] ?? 'Choose Folder Icon Style'; + String get chooseDrawerButtonStyle => _localizedStrings['chooseDrawerButtonStyle'] ?? 'Choose Drawer Button Style'; + String get appIconPicker => _localizedStrings['appIconPicker'] ?? 'App Icon Picker'; + String get appLauncherIcon => _localizedStrings['appLauncherIcon'] ?? 'App Launcher Icon'; + String get logo => _localizedStrings['logo'] ?? 'Logo'; + String get logo1 => _localizedStrings['logo1'] ?? 'Logo 1'; + String get logo2 => _localizedStrings['logo2'] ?? 'Logo 2'; + String get logo3 => _localizedStrings['logo3'] ?? 'Logo 3'; + String get logo4 => _localizedStrings['logo4'] ?? 'Logo 4'; + String appIconSwitched(String title) => (_localizedStrings['appIconSwitched'] ?? 'App icon switched to {title} successfully!').replaceAll('{title}', title.toString()); + String get customFontLoaded => _localizedStrings['customFontLoaded'] ?? 'Custom font loaded'; + String get failedToLoadFont => _localizedStrings['failedToLoadFont'] ?? 'Failed to load the selected font file.'; + String get invalidFileType => _localizedStrings['invalidFileType'] ?? 'Invalid File Type'; + String get invalidFileTypeMessage => _localizedStrings['invalidFileTypeMessage'] ?? 'Please select a valid OpenType (.otf) or TrueType (.ttf) font file.'; + String get removeCustomFont => _localizedStrings['removeCustomFont'] ?? 'Remove Custom Font'; + String get customFontRemoved => _localizedStrings['customFontRemoved'] ?? 'Custom font removed.'; + String get pleaseSelectValidBackup => _localizedStrings['pleaseSelectValidBackup'] ?? 'Please select a valid .json settings backup file'; + String get systemAppDisabled => _localizedStrings['systemAppDisabled'] ?? 'System App Disabled'; + String failedToRequestSaf(String e) => (_localizedStrings['failedToRequestSaf'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get enterConnectionName => _localizedStrings['enterConnectionName'] ?? 'Please enter a connection name'; + String get enterServerAddress => _localizedStrings['enterServerAddress'] ?? 'Please enter server address / hostname'; + String connectionFailed(String e) => (_localizedStrings['connectionFailed'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get http => _localizedStrings['http'] ?? 'HTTP'; + String get httpsSecure => _localizedStrings['httpsSecure'] ?? 'HTTPS (Secure)'; + String get retryConnection => _localizedStrings['retryConnection'] ?? 'Retry Connection'; + String get uploadClipboardHere => _localizedStrings['uploadClipboardHere'] ?? 'Upload Clipboard Here'; + String get uploadLocalClipboard => _localizedStrings['uploadLocalClipboard'] ?? 'Upload local clipboard to server'; + String get newFolder => _localizedStrings['newFolder'] ?? 'Nueva Carpeta'; + String get folderName => _localizedStrings['folderName'] ?? 'Folder name'; + String get copyToLocalDevice => _localizedStrings['copyToLocalDevice'] ?? 'Copy to Local Device'; + String get moveToLocalDevice => _localizedStrings['moveToLocalDevice'] ?? 'Move to Local Device'; + String get deleteQuestion => _localizedStrings['deleteQuestion'] ?? 'Eliminar'; + String get navigation => _localizedStrings['navigation'] ?? 'Navigation'; + String get systemRoot => _localizedStrings['systemRoot'] ?? 'System Root'; + String get globalSearch => _localizedStrings['globalSearch'] ?? 'Global Search'; + String get serversAndTools => _localizedStrings['serversAndTools'] ?? 'Servers & Tools'; + String get privateWallet => _localizedStrings['privateWallet'] ?? 'Private Wallet'; + String get ftpServer => _localizedStrings['ftpServer'] ?? 'FTP Server'; + String get webSharing => _localizedStrings['webSharing'] ?? 'Web Sharing'; + String get addRemoteConnection => _localizedStrings['addRemoteConnection'] ?? 'Add Remote Connection'; + String get quickCategories => _localizedStrings['quickCategories'] ?? 'Quick Categories'; + String get addShortcut => _localizedStrings['addShortcut'] ?? 'Add Shortcut'; + String get customizationAndSettings => _localizedStrings['customizationAndSettings'] ?? 'Customization & Settings'; + String get lightMode => _localizedStrings['lightMode'] ?? 'Light Mode'; + String get darkMode => _localizedStrings['darkMode'] ?? 'Dark Mode'; + String get aboutNFile => _localizedStrings['aboutNFile'] ?? 'About NFile'; + String couldNotOpenLink(String url) => (_localizedStrings['couldNotOpenLink'] ?? '{url}\'').replaceAll('{url}', url.toString()); + String get starOnRepository => _localizedStrings['starOnRepository'] ?? 'Star on Repository'; + String get joinTelegram => _localizedStrings['joinTelegram'] ?? 'Join Telegram Channel'; + String get shareAppWithFriends => _localizedStrings['shareAppWithFriends'] ?? 'Share App with Friends'; + String get exploreGitHubSource => _localizedStrings['exploreGitHubSource'] ?? 'Explore GitHub Source Code'; + String get copedToClipboard => _localizedStrings['copedToClipboard'] ?? 'Copied to clipboard'; + String get cutToClipboard => _localizedStrings['cutToClipboard'] ?? 'Cut to clipboard'; + String copedNItems(int n) => (_localizedStrings['copedNItems'] ?? 'Copied {n} item(s)').replaceAll('{n}', n.toString()); + String cutNItems(int n) => (_localizedStrings['cutNItems'] ?? 'Cut {n} item(s)').replaceAll('{n}', n.toString()); + String copedToClipboardN(int n) => (_localizedStrings['copedToClipboardN'] ?? 'Copied {n} items to clipboard').replaceAll('{n}', n.toString()); + String copedLabelToClipboard(String label) => (_localizedStrings['copedLabelToClipboard'] ?? 'Copied {label} to clipboard').replaceAll('{label}', label.toString()); + String cutToClipboardN(int n) => (_localizedStrings['cutToClipboardN'] ?? 'Cut {n} items to clipboard').replaceAll('{n}', n.toString()); + String get copedSelected => _localizedStrings['copedSelected'] ?? 'Copied selected items'; + String get cutSelected => _localizedStrings['cutSelected'] ?? 'Cut selected items'; + String get pastedSuccessfully => _localizedStrings['pastedSuccessfully'] ?? 'Pasted successfully'; + String pastedNItems(int n) => (_localizedStrings['pastedNItems'] ?? 'Pasted {n} item(s)').replaceAll('{n}', n.toString()); + String pastedItemsTo(int count, String dest) => (_localizedStrings['pastedItemsTo'] ?? 'Pasted {count} items to {dest}').replaceAll('{count}', count.toString()).replaceAll('{dest}', dest.toString()); + String get noShareableItems => _localizedStrings['noShareableItems'] ?? 'No shareable items found.'; + String get noFilesToShare => _localizedStrings['noFilesToShare'] ?? 'No files available to share'; + String errorSharing(String e) => (_localizedStrings['errorSharing'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String errorPreparingFiles(String e) => (_localizedStrings['errorPreparingFiles'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String errorReadingSharedFile(String e) => (_localizedStrings['errorReadingSharedFile'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get fileNotFoundOrNotShareable => _localizedStrings['fileNotFoundOrNotShareable'] ?? 'File not found or not shareable.'; + String get cannotMoveIntoItself => _localizedStrings['cannotMoveIntoItself'] ?? 'Cannot move a folder inside itself or same location'; + String get cannotCopyIntoItself => _localizedStrings['cannotCopyIntoItself'] ?? 'Cannot copy a folder inside itself or same location'; + String movedSuccessfully(String name) => (_localizedStrings['movedSuccessfully'] ?? 'Moved {name} successfully').replaceAll('{name}', name.toString()); + String copedSuccessfully(String name) => (_localizedStrings['copedSuccessfully'] ?? 'Copied {name} successfully').replaceAll('{name}', name.toString()); + String failedToMove(String e) => (_localizedStrings['failedToMove'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String failedToCopy(String e) => (_localizedStrings['failedToCopy'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String failedToTransfer(String e) => (_localizedStrings['failedToTransfer'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String failedToConnectRemote(String e) => (_localizedStrings['failedToConnectRemote'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get pasteHere => _localizedStrings['pasteHere'] ?? 'Paste Here'; + String pasteHereN(int n) => (_localizedStrings['pasteHereN'] ?? 'Paste Here ({n})').replaceAll('{n}', n.toString()); + String get actionCancelled => _localizedStrings['actionCancelled'] ?? 'Action cancelled / Clipboard cleared'; + String get extractToCurrentFolder => _localizedStrings['extractToCurrentFolder'] ?? 'Extract to Current Folder'; + String get addFile => _localizedStrings['addFile'] ?? 'Add File'; + String get addCustomPath => _localizedStrings['addCustomPath'] ?? 'Add Custom Path'; + String customPaths(int n) => (_localizedStrings['customPaths'] ?? '{n} custom path(s)').replaceAll('{n}', n.toString()); + String get addFolderFileShortcut => _localizedStrings['addFolderFileShortcut'] ?? 'Add Folder / File Shortcut'; + String get customPathsTooltip => _localizedStrings['customPathsTooltip'] ?? 'Custom Paths'; + String get deleteShortcut => _localizedStrings['deleteShortcut'] ?? 'Delete Shortcut'; + String get restoreLocation => _localizedStrings['restoreLocation'] ?? 'Restore Location'; + String get excludeLocation => _localizedStrings['excludeLocation'] ?? 'Exclude Location'; + String get addNetworkConnection => _localizedStrings['addNetworkConnection'] ?? 'Add Network Connection'; + String get removeConnection => _localizedStrings['removeConnection'] ?? 'Remove Connection'; + String get selectMode => _localizedStrings['selectMode'] ?? 'Select Mode'; + String get viewAndSortOptions => _localizedStrings['viewAndSortOptions'] ?? 'View & Sort Options'; + String get storageVolumes => _localizedStrings['storageVolumes'] ?? 'Storage Volumes & SD Card'; + String get createNew => _localizedStrings['createNew'] ?? 'Create New'; + String get openWith => _localizedStrings['openWith'] ?? 'Open with...'; + String get justOnce => _localizedStrings['justOnce'] ?? 'Just once'; + String get always => _localizedStrings['always'] ?? 'Always'; + String get openWithApp => _localizedStrings['openWithApp'] ?? 'Open with App'; + String get shareComingSoon => _localizedStrings['shareComingSoon'] ?? 'Share coming soon'; + String get savedSuccessfully => _localizedStrings['savedSuccessfully'] ?? 'Saved successfully'; + String errorSaving(String e) => (_localizedStrings['errorSaving'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String errorLoading(String e) => (_localizedStrings['errorLoading'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get standardMode => _localizedStrings['standardMode'] ?? 'Standard Mode'; + String get lagFreeMode => _localizedStrings['lagFreeMode'] ?? 'Lag-Free Mode'; + String get continuous => _localizedStrings['continuous'] ?? 'Continuous'; + String get singlePage => _localizedStrings['singlePage'] ?? 'Single Page'; + String get vertical => _localizedStrings['vertical'] ?? 'Vertical'; + String get horizontal => _localizedStrings['horizontal'] ?? 'Horizontal'; + String get enableTextSelection => _localizedStrings['enableTextSelection'] ?? 'Enable Text Selection'; + String get displaySettings => _localizedStrings['displaySettings'] ?? 'Display Settings'; + String get emptySheet => _localizedStrings['emptySheet'] ?? 'Empty Sheet'; + String get htmlPreview => _localizedStrings['htmlPreview'] ?? 'HTML Preview'; + String get markdownPreview => _localizedStrings['markdownPreview'] ?? 'Markdown Preview'; + String get reload => _localizedStrings['reload'] ?? 'Reload'; + String get selectSyntax => _localizedStrings['selectSyntax'] ?? 'Select Syntax'; + String get findReplace => _localizedStrings['findReplace'] ?? 'Find / Replace'; + String get saveFile => _localizedStrings['saveFile'] ?? 'Save File'; + String get moreOptions => _localizedStrings['moreOptions'] ?? 'More Options'; + String defaultZoom(String pt) => (_localizedStrings['defaultZoom'] ?? 'Default Zoom ({pt})').replaceAll('{pt}', pt.toString()); + String syntax(String lang) => (_localizedStrings['syntax'] ?? 'Syntax ({lang})').replaceAll('{lang}', lang.toString()); + String get find => _localizedStrings['find'] ?? 'Find...'; + String get replaceWith => _localizedStrings['replaceWith'] ?? 'Replace with...'; + String get replaceAll => _localizedStrings['replaceAll'] ?? 'Replace All'; + String get undo => _localizedStrings['undo'] ?? 'Undo'; + String get redo => _localizedStrings['redo'] ?? 'Redo'; + String get fileSaved => _localizedStrings['fileSaved'] ?? 'File saved successfully'; + String errorLoadingFile(String e) => (_localizedStrings['errorLoadingFile'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String errorSavingFile(String e) => (_localizedStrings['errorSavingFile'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String replacedOccurrences(int n) => (_localizedStrings['replacedOccurrences'] ?? 'Replaced {n} occurrences').replaceAll('{n}', n.toString()); + String ftpServerStarted(String ip, int port) => (_localizedStrings['ftpServerStarted'] ?? '{port}\'').replaceAll('{ip}', ip.toString()).replaceAll('{port}', port.toString()); + String get ftpServerStopped => _localizedStrings['ftpServerStopped'] ?? 'FTP Server stopped successfully'; + String errorStartingFtp(String e) => (_localizedStrings['errorStartingFtp'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get stopServerBeforeConfig => _localizedStrings['stopServerBeforeConfig'] ?? 'Please stop the server before changing configuration'; + String get changePort => _localizedStrings['changePort'] ?? 'Change Port'; + String get portNumber => _localizedStrings['portNumber'] ?? 'Port Number'; + String get portHint => _localizedStrings['portHint'] ?? 'e.g., 9999'; + String get invalidPort => _localizedStrings['invalidPort'] ?? 'Invalid port number'; + String get setUsername => _localizedStrings['setUsername'] ?? 'Set Username'; + String get username => _localizedStrings['username'] ?? 'Username'; + String get usernameCannotBeEmpty => _localizedStrings['usernameCannotBeEmpty'] ?? 'Username cannot be empty'; + String get stopServerBeforeEditing => _localizedStrings['stopServerBeforeEditing'] ?? 'Stop the server before editing settings'; + String get ftpShortcutAdded => _localizedStrings['ftpShortcutAdded'] ?? 'FTP Server shortcut added to home screen!'; + String get changeDirectory => _localizedStrings['changeDirectory'] ?? 'Change directory'; + String get changePortOption => _localizedStrings['changePortOption'] ?? 'Change port'; + String get setUser => _localizedStrings['setUser'] ?? 'Set user'; + String get anonymousAccess => _localizedStrings['anonymousAccess'] ?? 'Anonymous access'; + String get createShortcut => _localizedStrings['createShortcut'] ?? 'Create shortcut'; + String get homeDirectory => _localizedStrings['homeDirectory'] ?? 'Home directory'; + String get userName => _localizedStrings['userName'] ?? 'User name'; + String get showHiddenFilesFtp => _localizedStrings['showHiddenFilesFtp'] ?? 'Show hidden files'; + String get ftpes => _localizedStrings['ftpes'] ?? 'FTPES'; + String get ftpesDescription => _localizedStrings['ftpesDescription'] ?? 'Secure FTP connection over explicit TLS'; + String webSharingStarted(String url) => (_localizedStrings['webSharingStarted'] ?? '{url}\'').replaceAll('{url}', url.toString()); + String get webSharingStopped => _localizedStrings['webSharingStopped'] ?? 'Local HTTP Sharing Server stopped.'; + String errorStartingWeb(String e) => (_localizedStrings['errorStartingWeb'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get internetCloudTunnel => _localizedStrings['internetCloudTunnel'] ?? 'Internet cloud tunnel online! Temporary link active.'; + String failedToStartCloud(String e) => (_localizedStrings['failedToStartCloud'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get linkCopied => _localizedStrings['linkCopied'] ?? 'Link copied to clipboard!'; + String get internetShareDeactivated => _localizedStrings['internetShareDeactivated'] ?? 'Internet Share Tunnel deactivated.'; + String get copyUrl => _localizedStrings['copyUrl'] ?? 'Copy URL'; + String get qrCode => _localizedStrings['qrCode'] ?? 'QR Code'; + String get copyLink => _localizedStrings['copyLink'] ?? 'Copy Link'; + String get noRecentFiles => _localizedStrings['noRecentFiles'] ?? 'No recent files'; + String get successfullyDeleted => _localizedStrings['successfullyDeleted'] ?? 'Successfully deleted items'; + String get folderIsEmpty => _localizedStrings['folderIsEmpty'] ?? 'Folder is empty'; + String get couldNotReadArchive => _localizedStrings['couldNotReadArchive'] ?? 'Could not read archive'; + String extractedItem(String name, String dest) => (_localizedStrings['extractedItem'] ?? 'Extracted {name} to {dest}').replaceAll('{name}', name.toString()).replaceAll('{dest}', dest.toString()); + String copiedPhysicalItems(int n) => (_localizedStrings['copiedPhysicalItems'] ?? '{n} item(s) copied to clipboard').replaceAll('{n}', n.toString()); + String addedSuccessfully(int n) => (_localizedStrings['addedSuccessfully'] ?? 'Successfully added {n} item(s) into archive').replaceAll('{n}', n.toString()); + String pastedCountItems(int n) => (_localizedStrings['pastedCountItems'] ?? 'Pasted {n} item(s) into archive').replaceAll('{n}', n.toString()); + String get createArchive => _localizedStrings['createArchive'] ?? 'Create Archive'; + String get archiveName => _localizedStrings['archiveName'] ?? 'Archive Name'; + String get archiveFormat => _localizedStrings['archiveFormat'] ?? 'Archive Format'; + String get passwordOptional => _localizedStrings['passwordOptional'] ?? 'Password (Optional)'; + String get splitVolumeSize => _localizedStrings['splitVolumeSize'] ?? 'Split Volume Size in MB (Optional)'; + String get leaveEmptyForSingle => _localizedStrings['leaveEmptyForSingle'] ?? 'Leave empty for single archive'; + String get createSeparateArchive => _localizedStrings['createSeparateArchive'] ?? 'Create separate archive for each file'; + String createArchiveFailed(String e) => (_localizedStrings['createArchiveFailed'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get extractToFolder => _localizedStrings['extractToFolder'] ?? 'Extract to Folder'; + String get passwordIfEncrypted => _localizedStrings['passwordIfEncrypted'] ?? 'Password (if encrypted)'; + String get cancelPaste => _localizedStrings['cancelPaste'] ?? 'Cancel Paste'; + String get renameFile => _localizedStrings['renameFile'] ?? 'Rename File'; + String get newFilename => _localizedStrings['newFilename'] ?? 'New filename'; + String get namePattern => _localizedStrings['namePattern'] ?? 'Name Pattern'; + String get extensionLabel => _localizedStrings['extensionLabel'] ?? 'Extension'; + String get padding => _localizedStrings['padding'] ?? 'Padding'; + String get startNumber => _localizedStrings['startNumber'] ?? 'Start Number'; + String get findText => _localizedStrings['findText'] ?? 'Find text'; + String get searchTerm => _localizedStrings['searchTerm'] ?? 'Search term'; + String get replacement => _localizedStrings['replacement'] ?? 'Replacement'; + String get originalName => _localizedStrings['originalName'] ?? 'Original name (%)'; + String get sequentialNumber => _localizedStrings['sequentialNumber'] ?? 'Sequential number (#)'; + String get tripleSequentialNumber => _localizedStrings['tripleSequentialNumber'] ?? 'Triple sequential number (###)'; + String get fileNameWithoutExtension => _localizedStrings['fileNameWithoutExtension'] ?? 'File name without extension ({n})'; + String get extensionWithDot => _localizedStrings['extensionWithDot'] ?? 'Extension with dot ({de})'; + String get extensionWithoutDot => _localizedStrings['extensionWithoutDot'] ?? 'Extension without dot ({e})'; + String get fullNameWithExtension => _localizedStrings['fullNameWithExtension'] ?? 'Full name with extension ({N})'; + String get background => _localizedStrings['background'] ?? 'Background'; + String get cancelOperation => _localizedStrings['cancelOperation'] ?? 'Cancel Operation'; + String get transferSpeed => _localizedStrings['transferSpeed'] ?? 'Transfer Speed'; + String get estTime => _localizedStrings['estTime'] ?? 'Est. Time'; + String get dataProcessed => _localizedStrings['dataProcessed'] ?? 'Data Processed'; + String get showInLocation => _localizedStrings['showInLocation'] ?? 'Show in location'; + String get copySelected => _localizedStrings['copySelected'] ?? 'Copy Selected'; + String get cutSelectedB => _localizedStrings['cutSelectedB'] ?? 'Cut Selected'; + String get archiveCompress => _localizedStrings['archiveCompress'] ?? 'Archive (Compress)'; + String get propertiesAndInfo => _localizedStrings['propertiesAndInfo'] ?? 'Properties & Info'; + String get deleteSelected => _localizedStrings['deleteSelected'] ?? 'Eliminar Seleccionados'; + String get enterAbsolutePath => _localizedStrings['enterAbsolutePath'] ?? 'Enter absolute path...'; + String pathNotFound(String path) => (_localizedStrings['pathNotFound'] ?? '{path}\'').replaceAll('{path}', path.toString()); + String copedPath(String path) => (_localizedStrings['copedPath'] ?? '{path}\'').replaceAll('{path}', path.toString()); + String get goToParentDirectory => _localizedStrings['goToParentDirectory'] ?? 'Go to Parent Directory'; + String get searchEllipsis => _localizedStrings['searchEllipsis'] ?? 'Search...'; + String get useRootAccess => _localizedStrings['useRootAccess'] ?? 'Use Root Access (Superuser)'; + String get grantShizukuAccess => _localizedStrings['grantShizukuAccess'] ?? 'Grant Shizuku Access (No Root)'; + String get howToSetupShizuku => _localizedStrings['howToSetupShizuku'] ?? 'How to setup Shizuku?'; + String get newTab => _localizedStrings['newTab'] ?? 'New Tab'; + String get duplicateTab => _localizedStrings['duplicateTab'] ?? 'Duplicate Tab'; + String get closeOtherTabs => _localizedStrings['closeOtherTabs'] ?? 'Close Other Tabs'; + String get closeTab => _localizedStrings['closeTab'] ?? 'Close Tab'; + String get allFiles => _localizedStrings['allFiles'] ?? 'All Files'; + String get documentsOnly => _localizedStrings['documentsOnly'] ?? 'Documents only'; + String get imagesOnly => _localizedStrings['imagesOnly'] ?? 'Images only'; + String get audioOnly => _localizedStrings['audioOnly'] ?? 'Audio only'; + String get videosOnly => _localizedStrings['videosOnly'] ?? 'Videos only'; + String get archivesOnly => _localizedStrings['archivesOnly'] ?? 'Archives only'; + String get extractingBundle => _localizedStrings['extractingBundle'] ?? 'Extracting package bundle for installation...'; + String get noInstallableApk => _localizedStrings['noInstallableApk'] ?? 'No installable APK found in package bundle'; + String failedToExtractBundle(String e) => (_localizedStrings['failedToExtractBundle'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get failedToTriggerInstaller => _localizedStrings['failedToTriggerInstaller'] ?? 'Failed to trigger split APK installer'; + String get sortBySize => _localizedStrings['sortBySize'] ?? 'Sort by Size'; + String get sortAlphabetically => _localizedStrings['sortAlphabetically'] ?? 'Sort Alphabetically'; + String get rescanStorage => _localizedStrings['rescanStorage'] ?? 'Rescan Storage'; + String get selectAll_ => _localizedStrings['selectAll_'] ?? 'Select All'; + String get refreshList => _localizedStrings['refreshList'] ?? 'Refresh List'; + String get uninstallAppsTitle => _localizedStrings['uninstallAppsTitle'] ?? 'Uninstall Apps'; + String confirmUninstallApps(int n) => (_localizedStrings['confirmUninstallApps'] ?? 'Are you sure you want to uninstall {n} selected app(s)?').replaceAll('{n}', n.toString()); + String get backingUpApps => _localizedStrings['backingUpApps'] ?? 'Backing up selected applications...'; + String backedUpApps(int n) => (_localizedStrings['backedUpApps'] ?? 'Successfully backed up {n} app(s) to NFile/Backups/Apps/').replaceAll('{n}', n.toString()); + String failedToBackupApps(String e) => (_localizedStrings['failedToBackupApps'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get launchApplication => _localizedStrings['launchApplication'] ?? 'Launch Application'; + String get systemSettingsDetails => _localizedStrings['systemSettingsDetails'] ?? 'System Settings / Details'; + String get backUpApk => _localizedStrings['backUpApk'] ?? 'Back Up APK'; + String get backingUpApk => _localizedStrings['backingUpApk'] ?? 'Backing up APK...'; + String get shareApkFile => _localizedStrings['shareApkFile'] ?? 'Share APK File'; + String get uninstallApplication => _localizedStrings['uninstallApplication'] ?? 'Uninstall Application'; + String get restoreInstallApp => _localizedStrings['restoreInstallApp'] ?? 'Restore / Install App'; + String get shareBackupFile => _localizedStrings['shareBackupFile'] ?? 'Share Backup File'; + String get deleteBackupFile => _localizedStrings['deleteBackupFile'] ?? 'Delete Backup File'; + String get newestFirst => _localizedStrings['newestFirst'] ?? 'Newest First'; + String get oldestFirst => _localizedStrings['oldestFirst'] ?? 'Oldest First'; + String get dateWise => _localizedStrings['dateWise'] ?? 'Date Wise'; + String get newestFirstGrouped => _localizedStrings['newestFirstGrouped'] ?? 'Newest First (Grouped per month)'; + String get oldestFirstGrouped => _localizedStrings['oldestFirstGrouped'] ?? 'Oldest First (Grouped per month)'; + String get sizeLargeFirst => _localizedStrings['sizeLargeFirst'] ?? 'Size (Large First)'; + String get sizeSmallFirst => _localizedStrings['sizeSmallFirst'] ?? 'Size (Small First)'; + String get lockOption => _localizedStrings['lockOption'] ?? 'Lock Option'; + String get secureImport => _localizedStrings['secureImport'] ?? 'Secure Import (Sandbox)'; + String get inPlaceScramble => _localizedStrings['inPlaceScramble'] ?? 'In-Place Scramble (Fast)'; + String get scramblingAndProtecting => _localizedStrings['scramblingAndProtecting'] ?? 'Scrambling & Protecting...'; + String get restored => _localizedStrings['restored'] ?? 'Restored'; + String failedToRestoreFile(String e) => (_localizedStrings['failedToRestoreFile'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get fileDeletedPermanently => _localizedStrings['fileDeletedPermanently'] ?? 'File deleted permanently.'; + String failedToDeleteFile(String e) => (_localizedStrings['failedToDeleteFile'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get decryptingSecurely => _localizedStrings['decryptingSecurely'] ?? 'Decrypting securely...'; + String failedToDecrypt(String e) => (_localizedStrings['failedToDecrypt'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get securityDetails => _localizedStrings['securityDetails'] ?? 'Security Details'; + String errorLoadingVault(String e) => (_localizedStrings['errorLoadingVault'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get restoreUnhide => _localizedStrings['restoreUnhide'] ?? 'Restore (Unhide)'; + String get details => _localizedStrings['details'] ?? 'Details'; + String get searchScrambledFiles => _localizedStrings['searchScrambledFiles'] ?? 'Search scrambled files...'; + String get permanentlyDeleteQuestion => _localizedStrings['permanentlyDeleteQuestion'] ?? 'Are you sure you want to permanently delete '; + String get clearAll => _localizedStrings['clearAll'] ?? 'Clear All'; + String get backspace => _localizedStrings['backspace'] ?? 'Backspace'; + String get playbackSpeed => _localizedStrings['playbackSpeed'] ?? 'Playback Speed'; + String get lockControls => _localizedStrings['lockControls'] ?? 'Lock Controls'; + String get repeatMode => _localizedStrings['repeatMode'] ?? 'Repeat Mode'; + String get copyUrlTooltip => _localizedStrings['copyUrlTooltip'] ?? 'Copy URL'; + String get mediaPathCopied => _localizedStrings['mediaPathCopied'] ?? 'Media path copied to clipboard.'; + String get volume => _localizedStrings['volume'] ?? 'Volume'; + String get brightness => _localizedStrings['brightness'] ?? 'Brightness'; + String get sortOptions => _localizedStrings['sortOptions'] ?? 'Sort Options'; + String get soundFX => _localizedStrings['soundFX'] ?? 'Sound FX'; + String get lyrics => _localizedStrings['lyrics'] ?? 'Lyrics'; + String get sleepTimer => _localizedStrings['sleepTimer'] ?? 'Sleep Timer'; + String get playingQueue => _localizedStrings['playingQueue'] ?? 'Playing Queue'; + String sleepTimerSet(int mins) => (_localizedStrings['sleepTimerSet'] ?? 'Sleep timer set for {mins} minutes.').replaceAll('{mins}', mins.toString()); + String mins(int m) => (_localizedStrings['mins'] ?? '{m} Minutes').replaceAll('{m}', m.toString()); + String get soundAndSpeedFX => _localizedStrings['soundAndSpeedFX'] ?? 'Sound & Speed FX'; + String get pitchAdjustment => _localizedStrings['pitchAdjustment'] ?? 'Pitch Adjustment'; + String get resetToDefault => _localizedStrings['resetToDefault'] ?? 'Reset to Default'; + String get backgroundPlaybackStopped => _localizedStrings['backgroundPlaybackStopped'] ?? 'Background playback stopped'; + String get backgroundPlaybackEnabled => _localizedStrings['backgroundPlaybackEnabled'] ?? 'Background playback enabled'; + String get viewSynchronizedLyrics => _localizedStrings['viewSynchronizedLyrics'] ?? 'View Synchronized Lyrics'; + String get soundFXAndEqualizer => _localizedStrings['soundFXAndEqualizer'] ?? 'Sound FX & Equalizer'; + String get setSleepTimer => _localizedStrings['setSleepTimer'] ?? 'Set Sleep Timer'; + String get audioFileInfo => _localizedStrings['audioFileInfo'] ?? 'Audio File Info'; + String get lyricsLoaded => _localizedStrings['lyricsLoaded'] ?? 'Lyrics loaded successfully'; + String get loadLrcFile => _localizedStrings['loadLrcFile'] ?? 'Load LRC File'; + String get noDataToExport => _localizedStrings['noDataToExport'] ?? 'No data to export.'; + String exportedTo(String path) => (_localizedStrings['exportedTo'] ?? 'Successfully exported to {path}').replaceAll('{path}', path.toString()); + String exportFailed(String e) => (_localizedStrings['exportFailed'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get noTablesFound => _localizedStrings['noTablesFound'] ?? 'No tables found in this database.'; + String get exportTableToCsv => _localizedStrings['exportTableToCsv'] ?? 'Export Table to CSV'; + String get searchRows => _localizedStrings['searchRows'] ?? 'Search rows...'; + String get noRowsFound => _localizedStrings['noRowsFound'] ?? 'No rows found'; + String get noSchemaLoaded => _localizedStrings['noSchemaLoaded'] ?? 'No schema details loaded.'; + String get sqlEditor => _localizedStrings['sqlEditor'] ?? 'SQL Editor'; + String get selectTemplate => _localizedStrings['selectTemplate'] ?? 'SELECT template'; + String get enterSelectQuery => _localizedStrings['enterSelectQuery'] ?? 'Enter SELECT query here...'; + String get exportResultsToCsv => _localizedStrings['exportResultsToCsv'] ?? 'Export Results to CSV'; + String get runQuery => _localizedStrings['runQuery'] ?? 'Run Query'; + String typeLabel(String type) => (_localizedStrings['typeLabel'] ?? '{type}\'').replaceAll('{type}', type.toString()); + String defaultLabel(String val) => (_localizedStrings['defaultLabel'] ?? '{val}\'').replaceAll('{val}', val.toString()); + String errorCreatingFolder(String e) => (_localizedStrings['errorCreatingFolder'] ?? '{e}\'').replaceAll('{e}', e.toString()); + String get errorShizukuNotRunning => _localizedStrings['errorShizukuNotRunning'] ?? 'Access denied. Shizuku is not running or authorized.'; + String get createFolder => _localizedStrings['createFolder'] ?? 'Create Folder'; + String get selectStorage => _localizedStrings['selectStorage'] ?? 'Select Storage'; + String get clearSelection => _localizedStrings['clearSelection'] ?? 'Clear Selection'; + String pinSelected(int n) => (_localizedStrings['pinSelected'] ?? 'Pin Selected ({n})').replaceAll('{n}', n.toString()); + String get pinThisFolder => _localizedStrings['pinThisFolder'] ?? 'Pin This Folder'; + String addSelected(int n) => (_localizedStrings['addSelected'] ?? 'Add Selected ({n})').replaceAll('{n}', n.toString()); + String get noPhysicalFilesToRename => _localizedStrings['noPhysicalFilesToRename'] ?? 'No physical files found to rename'; + String copedToClipboardWithName(String name) => (_localizedStrings['copedToClipboardWithName'] ?? 'Copied {name} to clipboard').replaceAll('{name}', name.toString()); + String cutToClipboardWithName(String name) => (_localizedStrings['cutToClipboardWithName'] ?? 'Cut {name} to clipboard').replaceAll('{name}', name.toString()); + String deletedItem(String name) => (_localizedStrings['deletedItem'] ?? 'Deleted {name}').replaceAll('{name}', name.toString()); + String noItemsFound(String type) => (_localizedStrings['noItemsFound'] ?? 'No {type} found').replaceAll('{type}', type.toString()); + String get calculatingSizes => _localizedStrings['calculatingSizes'] ?? 'Calculating sizes...'; + String get contains => _localizedStrings['contains'] ?? 'Contains'; + String get modified => _localizedStrings['modified'] ?? 'Modified'; + String get permissions => _localizedStrings['permissions'] ?? 'Permissions'; + String get itemsSelected => _localizedStrings['itemsSelected'] ?? 'Items Selected'; + String get totalSize => _localizedStrings['totalSize'] ?? 'Total Size'; + String get selectedPaths => _localizedStrings['selectedPaths'] ?? ''; + String get ftpServerNotification => _localizedStrings['ftpServerNotification'] ?? 'NFile FTP Server'; + String ftpRunningAt(String ip, int port) => (_localizedStrings['ftpRunningAt'] ?? '{port}\'').replaceAll('{ip}', ip.toString()).replaceAll('{port}', port.toString()); + String get ftpServerChannelName => _localizedStrings['ftpServerChannelName'] ?? 'FTP Server'; + String get ftpServerChannelDesc => _localizedStrings['ftpServerChannelDesc'] ?? 'Displays status of the background FTP Server'; + String get nfileAudioPlayer => _localizedStrings['nfileAudioPlayer'] ?? 'NFile Audio Player'; + String get nfileArchiveOperations => _localizedStrings['nfileArchiveOperations'] ?? 'NFile Archive Operations'; + String get archiveProgressDesc => _localizedStrings['archiveProgressDesc'] ?? 'Shows progress of file compression and extraction'; + String get nfileStorage => _localizedStrings['nfileStorage'] ?? 'NFile Storage'; + String get internalStorageViaNFile => _localizedStrings['internalStorageViaNFile'] ?? 'Internal storage via NFile'; + String get webSharingServer => _localizedStrings['webSharingServer'] ?? 'Web Sharing Server'; + String get webSharingServerDesc => _localizedStrings['webSharingServerDesc'] ?? 'Displays status of the background Web Sharing Server'; + String get nfileInternetWebShare => _localizedStrings['nfileInternetWebShare'] ?? 'NFile Internet Web Share'; + String get nfileLocalWebShare => _localizedStrings['nfileLocalWebShare'] ?? 'NFile Local Web Share'; + String runningAt(String url) => (_localizedStrings['runningAt'] ?? 'Running at {url}').replaceAll('{url}', url.toString()); + String get nfileVersion => _localizedStrings['nfileVersion'] ?? 'NFile v1.0.43'; + String get storageAnalyzer => _localizedStrings['storageAnalyzer'] ?? 'Storage Analyzer'; + String freeSpace(String size) => (_localizedStrings['freeSpace'] ?? '{size}\'').replaceAll('{size}', size.toString()); + String totalSpace(String size) => (_localizedStrings['totalSpace'] ?? '{size}\'').replaceAll('{size}', size.toString()); + String get type => _localizedStrings['type'] ?? 'Type'; + String get movedItemsSuccessfully => _localizedStrings['movedItemsSuccessfully'] ?? 'Moved items successfully'; + String get copiedItemsSuccessfully => _localizedStrings['copiedItemsSuccessfully'] ?? 'Copied items successfully'; + String archiveCreatedSuccessfully(String name, String format) => (_localizedStrings['archiveCreatedSuccessfully'] ?? 'Archive "{name}.{format}" created successfully!').replaceAll('{name}', name.toString()).replaceAll('{format}', format.toString()); + String folderContains(int f, int d) => (_localizedStrings['folderContains'] ?? '{f} subfolder(s), {d} file(s)').replaceAll('{f}', f.toString()).replaceAll('{d}', d.toString()); + String itemsSelectedCount(int c, int f, int d) => (_localizedStrings['itemsSelectedCount'] ?? '{c} items ({f} folder(s), {d} file(s))').replaceAll('{c}', c.toString()).replaceAll('{f}', f.toString()).replaceAll('{d}', d.toString()); + String get language => _localizedStrings['language'] ?? 'Language'; + String get languageSub => _localizedStrings['languageSub'] ?? 'Select application language'; + String get systemDefault => _localizedStrings['systemDefault'] ?? 'System default'; + String get spanish => _localizedStrings['spanish'] ?? 'Spanish'; + String get english => _localizedStrings['english'] ?? 'English'; + String get threeDotDisabledInfoSub => _localizedStrings['threeDotDisabledInfoSub'] ?? 'Choose what to show on the right side of files and folders when 3-dot is hidden'; + String get appExitBehaviorSub => _localizedStrings['appExitBehaviorSub'] ?? 'Choose between exit confirmation dialog or double-pressing back button to exit'; + String get noSettingsFound => _localizedStrings['noSettingsFound'] ?? 'No settings found'; + String get trySearchingAnotherKeyword => _localizedStrings['trySearchingAnotherKeyword'] ?? 'Try searching for another keyword'; + String get settingsCategories => _localizedStrings['settingsCategories'] ?? 'Settings Categories'; + String get showConfirmationDialog => _localizedStrings['showConfirmationDialog'] ?? 'Show confirmation dialog'; + String get vibrantOrange => _localizedStrings['vibrantOrange'] ?? 'Vibrant Orange'; + String get royalPurple => _localizedStrings['royalPurple'] ?? 'Royal Purple'; + String get emeraldGreen => _localizedStrings['emeraldGreen'] ?? 'Emerald Green'; + String get crimsonRed => _localizedStrings['crimsonRed'] ?? 'Crimson Red'; + String get amberGold => _localizedStrings['amberGold'] ?? 'Amber Gold'; + String get cyberpunkPink => _localizedStrings['cyberpunkPink'] ?? 'Cyberpunk Pink'; + String get sapphireBlue => _localizedStrings['sapphireBlue'] ?? 'Sapphire Blue'; + String get forestGreen => _localizedStrings['forestGreen'] ?? 'Forest Green'; + String get sunsetPeach => _localizedStrings['sunsetPeach'] ?? 'Sunset Peach'; + String get defaultLogo => _localizedStrings['defaultLogo'] ?? 'Default Logo'; + String get outfitModernSans => _localizedStrings['outfitModernSans'] ?? 'Outfit Modern Sans'; + String get jetBrainsTechMono => _localizedStrings['jetBrainsTechMono'] ?? 'JetBrains Tech Mono'; + String get montserratUrbanSans => _localizedStrings['montserratUrbanSans'] ?? 'Montserrat Urban Sans'; + String get customImportedFont => _localizedStrings['customImportedFont'] ?? 'Custom Imported Font'; + String get signatureDefaultFont => _localizedStrings['signatureDefaultFont'] ?? 'Signature Default (Lexend Deca)'; + String get signatureDefaultFontDesc => _localizedStrings['signatureDefaultFontDesc'] ?? 'Original NFile clean geometric look'; + String get outfitFontDesc => _localizedStrings['outfitFontDesc'] ?? 'Super sleek, minimal, and premium geometric aesthetic'; + String get jetBrainsFontDesc => _localizedStrings['jetBrainsFontDesc'] ?? 'Clean and futuristic developer monospaced look'; + String get montserratFontDesc => _localizedStrings['montserratFontDesc'] ?? 'Bold, modern, and striking typographic scale'; + String customFontTitle(String name) => (_localizedStrings['customFontTitle'] ?? 'Custom Font ({name})').replaceAll('{name}', name.toString()); + String get customFontDesc => _localizedStrings['customFontDesc'] ?? 'Your custom loaded font file'; + String get replaceCustomFontFile => _localizedStrings['replaceCustomFontFile'] ?? 'Replace Custom Font File'; + String get importCustomFontFile => _localizedStrings['importCustomFontFile'] ?? 'Import Custom Font File (.ttf/.otf)'; + String get noneHideInfo => _localizedStrings['noneHideInfo'] ?? 'None / Hide Info'; + String get noneHideInfoDesc => _localizedStrings['noneHideInfoDesc'] ?? 'Do not display additional information on the right side'; + String get dateTimeTitle => _localizedStrings['dateTimeTitle'] ?? 'Date & Time'; + String get dateTimeDesc => _localizedStrings['dateTimeDesc'] ?? 'Display the last modified date and time'; + String get fileSizeItemCount => _localizedStrings['fileSizeItemCount'] ?? 'File Size / Item Count'; + String get fileSizeItemCountDesc => _localizedStrings['fileSizeItemCountDesc'] ?? 'Display file size for files and item count for folders'; + String get confirmDialogTitle => _localizedStrings['confirmDialogTitle'] ?? 'Confirmation Dialog'; + String get confirmDialogDesc => _localizedStrings['confirmDialogDesc'] ?? 'Prompt for exit verification before closing'; + String get doublePressToExit => _localizedStrings['doublePressToExit'] ?? 'Double-Press to Exit'; + String get doublePressToExitDesc => _localizedStrings['doublePressToExitDesc'] ?? 'Tap the back button twice within a short window to exit'; + String get neverManuallyClean => _localizedStrings['neverManuallyClean'] ?? 'Never (Manually clean bin)'; + String get days7 => _localizedStrings['days7'] ?? '7 Days'; + String get days15 => _localizedStrings['days15'] ?? '15 Days'; + String get days30Recommended => _localizedStrings['days30Recommended'] ?? '30 Days (Recommended)'; + String get trashDeletionWarning => _localizedStrings['trashDeletionWarning'] ?? 'Items in the Recycle Bin will be permanently deleted after this duration.'; + String get fileExplorerAndNavigation => _localizedStrings['fileExplorerAndNavigation'] ?? 'File Explorer & Navigation'; + String get materialYouDynamic => _localizedStrings['materialYouDynamic'] ?? 'Material You (Dynamic Wallpaper Colors)'; + String get originalDefaultBlue => _localizedStrings['originalDefaultBlue'] ?? 'Original Default (Signature Blue)'; + String get classicSolid => _localizedStrings['classicSolid'] ?? 'Classic Solid (Material)'; + String get modernRounded => _localizedStrings['modernRounded'] ?? 'Modern Rounded (Material)'; + String get starredSpecial => _localizedStrings['starredSpecial'] ?? 'Starred Special (Material)'; + String get snippetDocument => _localizedStrings['snippetDocument'] ?? 'Snippet Document (Material)'; + String get minimalOutlined => _localizedStrings['minimalOutlined'] ?? 'Minimal Outlined (Material)'; + String get nfileBrokenOutline => _localizedStrings['nfileBrokenOutline'] ?? 'NFile Broken Outline (Default)'; + String get categoryGridVuesax => _localizedStrings['categoryGridVuesax'] ?? 'Category Grid / Vuesax Grid'; + String get chooseTrailingInfoDesc => _localizedStrings['chooseTrailingInfoDesc'] ?? 'Choose what is displayed on the right side of files and folders when the 3-dot action buttons are hidden.'; + String get chooseAppLauncherIconDesc => _localizedStrings['chooseAppLauncherIconDesc'] ?? 'Choose a custom logo for the application launcher icon. Note that some launchers may take a few seconds to update.'; + String get nothingDotMatrix => _localizedStrings['nothingDotMatrix'] ?? 'Nothing Dot-Matrix & Sans'; + String get nothingDotMatrixDesc => _localizedStrings['nothingDotMatrixDesc'] ?? 'High-tech retro dot matrix headings + clean body'; + String get appTypographyTitle => _localizedStrings['appTypographyTitle'] ?? 'App Typography'; + String get selectTypefaceDesc => _localizedStrings['selectTypefaceDesc'] ?? "Select a beautiful typeface to customize NFile's overall visual theme"; + String get doublePressBackToExit => _localizedStrings['doublePressBackToExit'] ?? 'Double-press back button to exit'; + String get mediaAndDefaultActions => _localizedStrings['mediaAndDefaultActions'] ?? 'Media & Default Actions'; + String get hamburgerClassicMenu => _localizedStrings['hamburgerClassicMenu'] ?? 'Hamburger / Classic Menu'; + String get dotMatrixSans => _localizedStrings['dotMatrixSans'] ?? 'Dot-Matrix & Sans'; + String get neverAutoDeleteDisabled => _localizedStrings['neverAutoDeleteDisabled'] ?? 'Never (Auto-delete disabled)'; + String get after1Day => _localizedStrings['after1Day'] ?? 'After 1 Day'; + String get afterNDays => _localizedStrings['afterNDays'] ?? 'After {days} Days'; + String get uiOperationCancelled => _localizedStrings['uiOperationCancelled'] ?? 'Operation Cancelled'; + String get uiCompressionLimitExceeded => _localizedStrings['uiCompressionLimitExceeded'] ?? 'Compression Limit Exceeded'; + String get uiCompressingFiles => _localizedStrings['uiCompressingFiles'] ?? 'Compressing Files'; + String get uiExtractingArchive => _localizedStrings['uiExtractingArchive'] ?? 'Extracting Archive'; + String get uiExtremeSpeed => _localizedStrings['uiExtremeSpeed'] ?? 'Extreme Speed'; + String get uiStatelessCachingAsyncScans => _localizedStrings['uiStatelessCachingAsyncScans'] ?? 'Stateless caching & async scans'; + String get uiVaultSecure => _localizedStrings['uiVaultSecure'] ?? 'Vault Secure'; + String get uiEncryptedSafeWorkspace => _localizedStrings['uiEncryptedSafeWorkspace'] ?? 'Encrypted safe workspace'; + String get uiServersHub => _localizedStrings['uiServersHub'] ?? 'Servers Hub'; + String get uiFtpLanSftpWebdav => _localizedStrings['uiFtpLanSftpWebdav'] ?? 'FTP, LAN, SFTP & WebDAV'; + String get uiRichUi => _localizedStrings['uiRichUi'] ?? 'Rich UI'; + String get uiAmoledBlackBeautifulSeeds => _localizedStrings['uiAmoledBlackBeautifulSeeds'] ?? 'AMOLED Black & beautiful seeds'; + String get uiDeleteFile => _localizedStrings['uiDeleteFile'] ?? 'Delete File'; + String get uiNewFile => _localizedStrings['uiNewFile'] ?? 'New File'; + String get uiBestForTextDocuments => _localizedStrings['uiBestForTextDocuments'] ?? 'Best for text documents'; + String get uiBestForBrochuresPhotos => _localizedStrings['uiBestForBrochuresPhotos'] ?? 'Best for brochures & photos'; + String get uiPageLayout => _localizedStrings['uiPageLayout'] ?? 'Page Layout'; + String get uiScrollDirection => _localizedStrings['uiScrollDirection'] ?? 'Scroll Direction'; + + String get uiDownloadsFileLocalClipboard => _localizedStrings['uiDownloadsFileLocalClipboard'] ?? 'Downloads file → local clipboard'; + String get uiDownloadsAndDeletesFromServer => _localizedStrings['uiDownloadsAndDeletesFromServer'] ?? 'Downloads and deletes from server'; + String get uiApplications => _localizedStrings['uiApplications'] ?? 'Applications'; + String get uiImages => _localizedStrings['uiImages'] ?? 'Images'; + String get uiVideos => _localizedStrings['uiVideos'] ?? 'Videos'; + String get uiAudio => _localizedStrings['uiAudio'] ?? 'Audio'; + String get uiDocuments => _localizedStrings['uiDocuments'] ?? 'Documents'; + String get uiSystemOther => _localizedStrings['uiSystemOther'] ?? 'System / Other'; + String get uiEgImage => _localizedStrings['uiEgImage'] ?? 'e.g. Image_#'; + String get uiEg3 => _localizedStrings['uiEg3'] ?? 'e.g. 3'; + String get uiEg1 => _localizedStrings['uiEg1'] ?? 'e.g. 1'; + String get uiExistingFile => _localizedStrings['uiExistingFile'] ?? 'Existing File'; + String get uiDroppedFolder => _localizedStrings['uiDroppedFolder'] ?? 'Dropped Folder'; + String get uiCurrentFolder => _localizedStrings['uiCurrentFolder'] ?? 'Current Folder'; + String get uiMoveHere => _localizedStrings['uiMoveHere'] ?? 'Move here'; + String get uiCutPasteItemIntoDestinationFolder => _localizedStrings['uiCutPasteItemIntoDestinationFolder'] ?? 'Cut & paste item into destination folder'; + String get uiCopyHere => _localizedStrings['uiCopyHere'] ?? 'Copy here'; + String get uiLeavesOriginalFileIntactAndDuplicatesHere => _localizedStrings['uiLeavesOriginalFileIntactAndDuplicatesHere'] ?? 'Leaves original file intact and duplicates here'; + String get uiCompressItemIntoAZiptarArchiveHere => _localizedStrings['uiCompressItemIntoAZiptarArchiveHere'] ?? 'Compress item into a zip/tar archive here'; + String get uiShowAllFilesAndFoldersInThisDirectory => _localizedStrings['uiShowAllFilesAndFoldersInThisDirectory'] ?? 'Show all files and folders in this directory'; + String get uiPdfsWordDocsSpreadsheetsTextsAndEbooks => _localizedStrings['uiPdfsWordDocsSpreadsheetsTextsAndEbooks'] ?? 'PDFs, Word docs, spreadsheets, texts, and e-books'; + String get uiJpegsPngsWebpsAndRawPhotoFormats => _localizedStrings['uiJpegsPngsWebpsAndRawPhotoFormats'] ?? 'JPEGs, PNGs, WebPs, and raw photo formats'; + String get uiMp3sWavsAacsAndHighfidelityAudios => _localizedStrings['uiMp3sWavsAacsAndHighfidelityAudios'] ?? 'MP3s, WAVs, AACs, and high-fidelity audios'; + String get uiMp4sMkvsWebmsAndHighresVideoClips => _localizedStrings['uiMp4sMkvsWebmsAndHighresVideoClips'] ?? 'MP4s, MKVs, WebMs, and high-res video clips'; + String get uiZips7zsRarsAndOtherCompressedAssets => _localizedStrings['uiZips7zsRarsAndOtherCompressedAssets'] ?? 'ZIPs, 7Zs, RARs, and other compressed assets'; + String get uiName => _localizedStrings['uiName'] ?? 'Name'; + String get uiPath => _localizedStrings['uiPath'] ?? 'Path'; + String get uiSize => _localizedStrings['uiSize'] ?? 'Size'; + String get uiPreparingFoldersForSharing => _localizedStrings['uiPreparingFoldersForSharing'] ?? 'Preparing folders for sharing...'; + String get uiCompressingContentsPleaseWait => _localizedStrings['uiCompressingContentsPleaseWait'] ?? 'Compressing contents, please wait'; + String get uiCoreHighlights => _localizedStrings['uiCoreHighlights'] ?? 'Core Highlights'; + String get uiConnectShare => _localizedStrings['uiConnectShare'] ?? 'Connect & Share'; + String get uiNewlyCreatedOrDownloadedFilesWill => _localizedStrings['uiNewlyCreatedOrDownloadedFilesWill'] ?? 'Newly created or downloaded files will show up here.'; + String get uiSqliteDatabaseReader => _localizedStrings['uiSqliteDatabaseReader'] ?? 'SQLite Database Reader'; + String get uiFailedToOpenDatabase => _localizedStrings['uiFailedToOpenDatabase'] ?? 'Failed to open database'; + String get uiPk => _localizedStrings['uiPk'] ?? 'PK'; + String get uiNotNull => _localizedStrings['uiNotNull'] ?? 'NOT NULL'; + String get uiCreateANewDirectory => _localizedStrings['uiCreateANewDirectory'] ?? 'Create a new directory'; + String get uiCreateANewEmptyTextDocument => _localizedStrings['uiCreateANewEmptyTextDocument'] ?? 'Create a new empty text document'; + String get uiNewArchive => _localizedStrings['uiNewArchive'] ?? 'New Archive'; + String get uiCompressCurrentFolderContents => _localizedStrings['uiCompressCurrentFolderContents'] ?? 'Compress current folder contents'; + String get uiLayoutMode => _localizedStrings['uiLayoutMode'] ?? 'Layout Mode'; + String get uiListView => _localizedStrings['uiListView'] ?? 'List View'; + String get uiGridView => _localizedStrings['uiGridView'] ?? 'Grid View'; + String get uiSizePaddingOptions => _localizedStrings['uiSizePaddingOptions'] ?? 'Size & Padding Options'; + String get uiIconFolderSize => _localizedStrings['uiIconFolderSize'] ?? 'Icon & Folder Size'; + String get uiItemPaddingSpacing => _localizedStrings['uiItemPaddingSpacing'] ?? 'Item Padding & Spacing'; + String get uiSortBy => _localizedStrings['uiSortBy'] ?? 'Sort By'; + String get uiOnlyThisFolder => _localizedStrings['uiOnlyThisFolder'] ?? 'Only this folder'; + String get uiEnableCustomSortingSpecificToThis => _localizedStrings['uiEnableCustomSortingSpecificToThis'] ?? 'Enable custom sorting specific to this folder'; + String get uiStorageVolumes => _localizedStrings['uiStorageVolumes'] ?? 'Storage Volumes'; + String get uiNetworkConnections => _localizedStrings['uiNetworkConnections'] ?? 'Network Connections'; + String get uiNoResultsFound => _localizedStrings['uiNoResultsFound'] ?? 'No results found'; + String get uiEmptyFolder => _localizedStrings['uiEmptyFolder'] ?? 'Empty Folder'; + String get uiThisDirectoryDoesNotContainAny => _localizedStrings['uiThisDirectoryDoesNotContainAny'] ?? 'This directory does not contain any files or subfolders.'; + String get uiPastedHoldingClipboardForMultiplePastes => _localizedStrings['uiPastedHoldingClipboardForMultiplePastes'] ?? 'Pasted (holding clipboard for multiple pastes)'; + String get uiFiles => _localizedStrings['uiFiles'] ?? 'Files'; + String get uiPdfDisplaySettings => _localizedStrings['uiPdfDisplaySettings'] ?? 'PDF Display Settings'; + String get uiOptimizeRenderingPerformanceForLargeDesignheavy => _localizedStrings['uiOptimizeRenderingPerformanceForLargeDesignheavy'] ?? 'Optimize rendering performance for large, design-heavy, or scanned documents.'; + String get uiQuickPerformancePresets => _localizedStrings['uiQuickPerformancePresets'] ?? 'Quick Performance Presets'; + String get uiDetailedTuningOptions => _localizedStrings['uiDetailedTuningOptions'] ?? 'Detailed Tuning Options'; + String get uiDisableToSignificantlyBoostPageRendering => _localizedStrings['uiDisableToSignificantlyBoostPageRendering'] ?? 'Disable to significantly boost page rendering speed and eliminate scroll stutter.'; + String get uiNetworkStatus => _localizedStrings['uiNetworkStatus'] ?? 'Network status'; + String get uiConnected => _localizedStrings['uiConnected'] ?? 'Connected'; + String get uiServerAddress => _localizedStrings['uiServerAddress'] ?? 'Server address'; + String get uiFailedToLoadImage => _localizedStrings['uiFailedToLoadImage'] ?? 'Failed to load image'; + String get uiSelectStorageDrive => _localizedStrings['uiSelectStorageDrive'] ?? 'Select Storage Drive'; + String get uiLongPressToOpenWith => _localizedStrings['uiLongPressToOpenWith'] ?? 'Long press to Open with...'; + String get uiAllItems => _localizedStrings['uiAllItems'] ?? 'All Items'; + String get uiFolders => _localizedStrings['uiFolders'] ?? 'Folders'; + String get uiRemoteConnections => _localizedStrings['uiRemoteConnections'] ?? 'Remote Connections'; + String get uiSelectNetworkService => _localizedStrings['uiSelectNetworkService'] ?? 'Select Network Service'; + String get uiMountARemoteServerOrNas => _localizedStrings['uiMountARemoteServerOrNas'] ?? 'Mount a remote server or NAS share as a dynamic drive within your NFile storage lists.'; + String get uiEnterConnectionDetailsToLinkThis => _localizedStrings['uiEnterConnectionDetailsToLinkThis'] ?? 'Enter connection details to link this network volume.'; + String get uiCreatingMountPoint => _localizedStrings['uiCreatingMountPoint'] ?? 'Creating Mount Point...'; + String get uiRecycleBinIsEmpty => _localizedStrings['uiRecycleBinIsEmpty'] ?? 'Recycle Bin is Empty'; + String get uiItemsYouDeleteWhenRecycleBin => _localizedStrings['uiItemsYouDeleteWhenRecycleBin'] ?? 'Items you delete when Recycle Bin is enabled will appear here. You can restore them or permanently delete them.'; + String get uiNewRemoteFolder => _localizedStrings['uiNewRemoteFolder'] ?? 'New Remote Folder'; + String get uiConnectionLost => _localizedStrings['uiConnectionLost'] ?? 'Connection Lost'; + String get uiEmptyDirectory => _localizedStrings['uiEmptyDirectory'] ?? 'Empty Directory'; + String get uiChooseProtectionMode => _localizedStrings['uiChooseProtectionMode'] ?? 'Choose Protection Mode'; + String get uiChooseHowYouWantToProtect => _localizedStrings['uiChooseHowYouWantToProtect'] ?? 'Choose how you want to protect your selected files. Secured files are XOR scrambled instantly.'; + String get uiActive => _localizedStrings['uiActive'] ?? 'Active'; + String get uiHideFiles => _localizedStrings['uiHideFiles'] ?? 'Hide Files'; + String get uiSecurityStorage => _localizedStrings['uiSecurityStorage'] ?? 'SECURITY STORAGE'; + String get uiTotalSpaceSecured => _localizedStrings['uiTotalSpaceSecured'] ?? 'Total Space Secured'; + String get uiHiddenFiles => _localizedStrings['uiHiddenFiles'] ?? 'Hidden Files'; + String get uiEstablishingSecureProxyRelay => _localizedStrings['uiEstablishingSecureProxyRelay'] ?? 'Establishing secure proxy relay...'; + String get uiScanQrCode => _localizedStrings['uiScanQrCode'] ?? 'Scan QR Code'; + String get uiWebSharingHub => _localizedStrings['uiWebSharingHub'] ?? 'Web Sharing Hub'; + String get uiLocalWebShare => _localizedStrings['uiLocalWebShare'] ?? 'Local Web Share'; + String get uiInternetShareLink => _localizedStrings['uiInternetShareLink'] ?? 'Internet Share Link'; + String get uiHttpLocalShareServer => _localizedStrings['uiHttpLocalShareServer'] ?? 'HTTP Local Share Server'; + String get uiAllowsOtherDevicesOnTheSame => _localizedStrings['uiAllowsOtherDevicesOnTheSame'] ?? 'Allows other devices on the same Wi-Fi to access, view, and stream your files in their web browser.'; + String get uiServerOnlineStreaming => _localizedStrings['uiServerOnlineStreaming'] ?? 'Server Online & Streaming'; + String get uiDirectBrowserUrl => _localizedStrings['uiDirectBrowserUrl'] ?? 'Direct Browser URL:'; + String get uiServerIsIdle => _localizedStrings['uiServerIsIdle'] ?? 'Server is Idle'; + String get uiMakeSureOtherDevicesAreOn => _localizedStrings['uiMakeSureOtherDevicesAreOn'] ?? 'Make sure other devices are on the same Wi-Fi network as this device, then start the server.'; + String get uiInternetShareTunnel => _localizedStrings['uiInternetShareTunnel'] ?? 'Internet Share Tunnel'; + String get uiGeneratesASecureTemporaryPublicTunnel => _localizedStrings['uiGeneratesASecureTemporaryPublicTunnel'] ?? 'Generates a secure temporary public tunnel link. Share this link with anyone anywhere on the internet to let them download files high-speed, no matter the file size.'; + String get uiCloudTunnelActive => _localizedStrings['uiCloudTunnelActive'] ?? 'Cloud Tunnel Active'; + String get uiTemporaryShareLinkActive24h => _localizedStrings['uiTemporaryShareLinkActive24h'] ?? 'Temporary Share Link (Active 24h):'; + String get uiConnectedBrowserClients => _localizedStrings['uiConnectedBrowserClients'] ?? 'Connected Browser Clients'; + String get uiWaitingForIncomingInternetDownloads => _localizedStrings['uiWaitingForIncomingInternetDownloads'] ?? 'Waiting for incoming internet downloads...'; + String get uiInternetSharingInactive => _localizedStrings['uiInternetSharingInactive'] ?? 'Internet Sharing Inactive'; + String get uiActivateTheTunnelToEstablishA => _localizedStrings['uiActivateTheTunnelToEstablishA'] ?? 'Activate the tunnel to establish a secure link that works beyond local Wi-Fi.'; + String get uiLosslessAudio => _localizedStrings['uiLosslessAudio'] ?? 'Lossless Audio'; + String get uiNoSynchronizedLyricsFound => _localizedStrings['uiNoSynchronizedLyricsFound'] ?? 'No Synchronized Lyrics Found'; + String get uiKeepALrcFileWithThe => _localizedStrings['uiKeepALrcFileWithThe'] ?? 'Keep a .lrc file with the exact same name next to your song, or select it manually below.'; + String get uiTapALineToSeekPlayback => _localizedStrings['uiTapALineToSeekPlayback'] ?? 'Tap a line to seek playback'; + String get uiAppManager => _localizedStrings['uiAppManager'] ?? 'App Manager'; + String get uiExactStorageCalculation => _localizedStrings['uiExactStorageCalculation'] ?? 'Exact Storage Calculation'; + String get uiToSeeExactAppStorageSizes => _localizedStrings['uiToSeeExactAppStorageSizes'] ?? 'To see exact app storage sizes (APK + data + cache) instead of just the raw installer size, please enable the Usage Access permission for NFile in System Settings.'; + String get uiGrantUsageAccessPermission => _localizedStrings['uiGrantUsageAccessPermission'] ?? 'Grant Usage Access Permission'; + String get uiStorageAnalytics => _localizedStrings['uiStorageAnalytics'] ?? 'Storage Analytics'; + String get uiScanningDeviceStorage => _localizedStrings['uiScanningDeviceStorage'] ?? 'Scanning Device Storage'; + String get uiAnalyzingFilesCategorizingAssetsAndReading => _localizedStrings['uiAnalyzingFilesCategorizingAssetsAndReading'] ?? 'Analyzing files, categorizing assets, and reading installed apps space...'; + String get uiTotalStorage => _localizedStrings['uiTotalStorage'] ?? 'Total Storage'; + String get uiBreakdown => _localizedStrings['uiBreakdown'] ?? 'Breakdown'; + String get uiNoApplicationsFound => _localizedStrings['uiNoApplicationsFound'] ?? 'No applications found'; + String get uiNoBackupsFound => _localizedStrings['uiNoBackupsFound'] ?? 'No backups found'; + String get uiSlideTapToUnlock => _localizedStrings['uiSlideTapToUnlock'] ?? 'Slide / Tap to Unlock'; + String get uiHwDec => _localizedStrings['uiHwDec'] ?? 'HW Dec'; + String get uiOverallProgress => _localizedStrings['uiOverallProgress'] ?? 'Overall Progress'; + String get uiRenamingFiles => _localizedStrings['uiRenamingFiles'] ?? 'Renaming files...'; + String get uiPleaseWaitUpdatingFolderContent => _localizedStrings['uiPleaseWaitUpdatingFolderContent'] ?? 'Please wait, updating folder content'; + String get uiBatchRename => _localizedStrings['uiBatchRename'] ?? 'Batch Rename'; + String get uiRenamePreview => _localizedStrings['uiRenamePreview'] ?? 'Rename Preview'; + String get uiBackToEdit => _localizedStrings['uiBackToEdit'] ?? 'Back to Edit'; + String get uiApplyChanges => _localizedStrings['uiApplyChanges'] ?? 'Apply Changes'; + String get uiFileAlreadyExists => _localizedStrings['uiFileAlreadyExists'] ?? 'File Already Exists'; + String get uiApplyToAllRemainingConflicts => _localizedStrings['uiApplyToAllRemainingConflicts'] ?? 'Apply to all remaining conflicts'; + String get uiNewer => _localizedStrings['uiNewer'] ?? 'Newer'; + String get uiDragDropOptions => _localizedStrings['uiDragDropOptions'] ?? 'Drag & Drop Options'; + String get uiDestinationLocation => _localizedStrings['uiDestinationLocation'] ?? 'Destination Location'; + String get uiChooseAction => _localizedStrings['uiChooseAction'] ?? 'Choose Action'; + String get uiExtractArchive => _localizedStrings['uiExtractArchive'] ?? 'Extract Archive'; + String get uiFilterFilesByType => _localizedStrings['uiFilterFilesByType'] ?? 'Filter Files By Type'; + String get uiSelectACategoryToDisplayMatching => _localizedStrings['uiSelectACategoryToDisplayMatching'] ?? 'Select a category to display matching files only'; + String get uiNoMatchingDirectoriesOrFilesFound => _localizedStrings['uiNoMatchingDirectoriesOrFilesFound'] ?? 'No matching directories or files found'; + String get uiBuiltinNfileViewer => _localizedStrings['uiBuiltinNfileViewer'] ?? 'Built-in NFile Viewer'; + String get uiSystemExternalApp => _localizedStrings['uiSystemExternalApp'] ?? 'System External App'; + String get uiOpenWithThirdPartyAppsOn => _localizedStrings['uiOpenWithThirdPartyAppsOn'] ?? 'Open with third party apps on device'; + String get uiSearchInTab => _localizedStrings['uiSearchInTab'] ?? 'Search in tab'; + String get uiInternalStorage => _localizedStrings['uiInternalStorage'] ?? 'Internal Storage'; + String get uiBrowseDeviceFiles => _localizedStrings['uiBrowseDeviceFiles'] ?? 'Browse device files'; + String get uiCustomize => _localizedStrings['uiCustomize'] ?? 'Customize'; + String get uiNoShortcutsPinnedTapCustomizeTo => _localizedStrings['uiNoShortcutsPinnedTapCustomizeTo'] ?? 'No shortcuts pinned. Tap Customize to add.'; + String get uiDragItemsByTheHandleTo => _localizedStrings['uiDragItemsByTheHandleTo'] ?? 'Drag items by the handle (=) to reorder icons on the Home Screen.'; + String get uiDefaultScanLocations => _localizedStrings['uiDefaultScanLocations'] ?? 'Default Scan Locations:'; + String get uiCustomScanLocations => _localizedStrings['uiCustomScanLocations'] ?? 'Custom Scan Locations:'; + String get uiNoCustomPathsAdded => _localizedStrings['uiNoCustomPathsAdded'] ?? 'No custom paths added.'; + String get uiRecentFiles => _localizedStrings['uiRecentFiles'] ?? 'Recent Files'; + String get uiViewAll => _localizedStrings['uiViewAll'] ?? 'View All'; + String get uiRestrictedSystemFolder => _localizedStrings['uiRestrictedSystemFolder'] ?? 'Restricted System Folder'; + String get uiAndroid11RestrictsStandardAccessTo => _localizedStrings['uiAndroid11RestrictsStandardAccessTo'] ?? 'Android 11+ restricts standard access to Android/data and Android/obb folders to protect app data. To view and modify these files, NFile requires advanced permissions.'; + + String deletePermanentlyFromServer(String name) => (_localizedStrings['deletePermanentlyFromServer'] ?? 'Delete "{name}" permanently from the server?').replaceAll('{name}', name.toString()); + + String get vaultEnterPin => _localizedStrings['vaultEnterPin'] ?? 'Enter PIN to Unlock Wallet'; + String get vaultSetPin => _localizedStrings['vaultSetPin'] ?? 'Set your 4-digit Wallet PIN'; + String get vaultConfirmPin => _localizedStrings['vaultConfirmPin'] ?? 'Confirm your 4-digit PIN'; + String get vaultPinSuccess => _localizedStrings['vaultPinSuccess'] ?? 'PIN Set Successfully!'; + String get vaultPinMismatch => _localizedStrings['vaultPinMismatch'] ?? 'PINs do not match. Try again!'; + String get vaultPinIncorrect => _localizedStrings['vaultPinIncorrect'] ?? 'Incorrect PIN. Try again!'; + String get actionStop => _localizedStrings['actionStop'] ?? 'Stop'; + String get actionStart => _localizedStrings['actionStart'] ?? 'Start'; + String get actionAnonymous => _localizedStrings['actionAnonymous'] ?? 'Anonymous'; + String sharingDirectory(String dir) => (_localizedStrings['sharingDirectory'] ?? 'Sharing Directory: {dir}').replaceAll('{dir}', dir.toString()); + String get stopWebServer => _localizedStrings['stopWebServer'] ?? 'Stop Web Server'; + String get startWebServer => _localizedStrings['startWebServer'] ?? 'Start Web Server'; + String get protocolDescSmb => _localizedStrings['protocolDescSmb'] ?? 'Local Area Network & SMB NAS Share'; + String get protocolDescFtp => _localizedStrings['protocolDescFtp'] ?? 'Standard File Transfer Protocol'; + String get protocolDescSftp => _localizedStrings['protocolDescSftp'] ?? 'SSH Secure File Transfer Server'; + String get protocolDescWebDav => _localizedStrings['protocolDescWebDav'] ?? 'HTTP Web Distributed Authoring'; + String get protocolDescSaf => _localizedStrings['protocolDescSaf'] ?? 'Android Storage Access Framework (SD Card / External)'; + + String get sortNameAsc => _localizedStrings['sortNameAsc'] ?? 'Name (A-Z)'; + String get sortNameDesc => _localizedStrings['sortNameDesc'] ?? 'Name (Z-A)'; + String get sortNewest => _localizedStrings['sortNewest'] ?? 'Newest'; + String get sortOldest => _localizedStrings['sortOldest'] ?? 'Oldest'; + String get sortSizeLarge => _localizedStrings['sortSizeLarge'] ?? 'Size (Large)'; + String get sortSizeSmall => _localizedStrings['sortSizeSmall'] ?? 'Size (Small)'; + String get sortType => _localizedStrings['sortType'] ?? 'Type'; + + String get aboutCopyright => _localizedStrings['aboutCopyright'] ?? 'Copyright 2026 NFile. All rights reserved.'; + String get aboutDescription => _localizedStrings['aboutDescription'] ?? 'NFile is a beautiful file manager.'; + String get aboutMadeWith => _localizedStrings['aboutMadeWith'] ?? 'Made with love by Rubex'; + String get aboutVersion => _localizedStrings['aboutVersion'] ?? 'v1.0.42 (Stable)'; + String get noMatchesFound => _localizedStrings['noMatchesFound'] ?? 'We could not find anything matching '; + String get videoCodecInfo => _localizedStrings['videoCodecInfo'] ?? 'AVC / AAC - 1080p'; + String get conflictFileExists => _localizedStrings['conflictFileExists'] ?? 'A file named '; + String copiedItems(int count) => (_localizedStrings['copiedItems'] ?? 'Copied {count} item(s)').replaceAll('{count}', count.toString()); + String cutItems(int count) => (_localizedStrings['cutItems'] ?? 'Cut {count} item(s)').replaceAll('{count}', count.toString()); + String playingQueueCount(int count) => (_localizedStrings['playingQueueCount'] ?? 'Playing Queue ({count})').replaceAll('{count}', count.toString()); + String configuringItems(int count) => (_localizedStrings['configuringItems'] ?? 'Configuring {count} items').replaceAll('{count}', count.toString()); + String reviewingItems(int count) => (_localizedStrings['reviewingItems'] ?? 'Reviewing {count} items').replaceAll('{count}', count.toString()); + String filesCount(int count) => (_localizedStrings['filesCount'] ?? 'files: {count}').replaceAll('{count}', count.toString()); + String foldersCount(int count) => (_localizedStrings['foldersCount'] ?? 'folders: {count}').replaceAll('{count}', count.toString()); + String queryReturned(int count) => (_localizedStrings['queryReturned'] ?? 'Query returned {count} rows').replaceAll('{count}', count.toString()); + String processingItem(int current, int total) => (_localizedStrings['processingItem'] ?? 'Processing item {current} of {total}').replaceAll('{current}', current.toString()).replaceAll('{total}', total.toString()); + String stepXofY(int step, int total) => (_localizedStrings['stepXofY'] ?? 'Step {step} of {total}').replaceAll('{step}', step.toString()).replaceAll('{total}', total.toString()); + String get webSearchPlaceholder => _localizedStrings['webSearchPlaceholder'] ?? 'Search files & folders...'; + String get webUploadBtn => _localizedStrings['webUploadBtn'] ?? 'Upload'; + String get webUploadTooltip => _localizedStrings['webUploadTooltip'] ?? 'Upload Files to this Folder'; + String get webParentDir => _localizedStrings['webParentDir'] ?? '.. (Parent Directory)'; + String get webGoUpLevel => _localizedStrings['webGoUpLevel'] ?? 'Go up one level'; + String get webNoResults => _localizedStrings['webNoResults'] ?? 'No items match your search'; + String get webCheckSpelling => _localizedStrings['webCheckSpelling'] ?? 'Check the spelling or try a different search term.'; + String get webFileName => _localizedStrings['webFileName'] ?? 'File Name'; + String get webCopyLink => _localizedStrings['webCopyLink'] ?? 'Copy Link'; + String get webDownload => _localizedStrings['webDownload'] ?? 'Download'; + String get webCloseModal => _localizedStrings['webCloseModal'] ?? 'Close Modal'; + String get webDownloadFile => _localizedStrings['webDownloadFile'] ?? 'Download File'; + String get webDropFiles => _localizedStrings['webDropFiles'] ?? 'Drop files here to upload'; + String get webUploadInstantly => _localizedStrings['webUploadInstantly'] ?? 'Your files will be uploaded instantly to this shared folder'; + String get webUploadingFile => _localizedStrings['webUploadingFile'] ?? 'Uploading file...'; + String get webSecurelySharing => _localizedStrings['webSecurelySharing'] ?? 'Securely sharing and streaming files via NFile'; + String get webUploadSuccess => _localizedStrings['webUploadSuccess'] ?? 'Upload completed successfully!'; + String get webLinkCopied => _localizedStrings['webLinkCopied'] ?? 'Link copied to clipboard!'; + String get webLinkCopyFailed => _localizedStrings['webLinkCopyFailed'] ?? 'Failed to copy link.'; + String get webUploadingName => _localizedStrings['webUploadingName'] ?? 'Uploading \${file.name}...'; + String get webUploadFailedName => _localizedStrings['webUploadFailedName'] ?? 'Failed to upload \${file.name}'; + String get webLoadingPreview => _localizedStrings['webLoadingPreview'] ?? 'Loading preview...'; + String get webStreamFailed => _localizedStrings['webStreamFailed'] ?? 'Failed to stream document. You can still download it directly.'; + String get webPreviewNotSupported => _localizedStrings['webPreviewNotSupported'] ?? 'Preview is not supported for this file type'; + String get webClickDownload => _localizedStrings['webClickDownload'] ?? 'Click Download below to save it on your system.'; + String get webVideoNotSupported => _localizedStrings['webVideoNotSupported'] ?? 'Your browser does not support the video streaming tag.'; + String get webAudioNotSupported => _localizedStrings['webAudioNotSupported'] ?? 'Your browser does not support the audio element.'; + String get webUploadFailed => _localizedStrings['webUploadFailed'] ?? 'Upload failed'; + String get webNetworkError => _localizedStrings['webNetworkError'] ?? 'Network error'; + +} + + +class _AppStringsDelegate extends LocalizationsDelegate { + const _AppStringsDelegate(); + + @override + bool isSupported(Locale locale) => ['en', 'es'].contains(locale.languageCode); + + @override + Future load(Locale locale) { + AppStrings._instance = AppStrings._(); + return Future.value(AppStrings._instance); + } + + @override + bool shouldReload(_AppStringsDelegate old) => false; +} diff --git a/lib/core/theme.dart b/lib/core/theme.dart index 5359bc4..0633443 100644 --- a/lib/core/theme.dart +++ b/lib/core/theme.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; class AppTheme { - static const Color seedColor = Color(0xFF369FE7); + static const Color seedColor = Color(0xFF6B7280); static ThemeData getAppTheme({ required bool light, @@ -21,31 +21,28 @@ class AppTheme { final baseScheme = ColorScheme.fromSeed( seedColor: rawColor, brightness: brightness, - contrastLevel: 0.05, - dynamicSchemeVariant: DynamicSchemeVariant.fidelity, - ); - colorScheme = baseScheme.copyWith( - primary: rawColor, + contrastLevel: 0.0, + dynamicSchemeVariant: DynamicSchemeVariant.neutral, ); + colorScheme = baseScheme; } - final effectivePrimary = colorScheme.primary; - final mainColorMultiplier = pitchBlack ? 0.1 : 0.8; - final pitchGrey = pitchBlack ? const Color.fromARGB(255, 20, 20, 20) : const Color.fromARGB(255, 35, 35, 35); - final pitchBlackColor = pitchBlack ? const Color.fromARGB(255, 0, 0, 0) : null; - - int getColorAlpha(int a) => (a * mainColorMultiplier).round(); - Color getMainColorWithAlpha(int a) => effectivePrimary.withAlpha(getColorAlpha(a)); + final scaffoldBg = light + ? const Color(0xFFFAFAFA) + : (pitchBlack ? const Color(0xFF000000) : const Color(0xFF0D0D0D)); + final cardBg = light + ? const Color(0xFFFFFFFF) + : (pitchBlack ? const Color(0xFF0A0A0A) : const Color(0xFF141414)); + final surfaceBg = light + ? const Color(0xFFF5F5F5) + : (pitchBlack ? const Color(0xFF080808) : const Color(0xFF111111)); - final cardColor = Color.alphaBlend( - getMainColorWithAlpha(35), - light ? const Color.fromARGB(255, 255, 255, 255) : pitchGrey, - ); + final borderColor = light + ? const Color(0xFFE5E5E5) + : const Color(0xFF2A2A2A); - // Map font keys to actual font families/themes - TextTheme? textTheme; String? effectiveFontFamily; - + TextTheme? textTheme; final baseTextTheme = ThemeData(brightness: brightness).textTheme; switch (fontFamily) { @@ -86,10 +83,13 @@ class AppTheme { break; case 'default': default: - effectiveFontFamily = 'LexendDeca'; + effectiveFontFamily = null; break; } + final textColor = light ? const Color(0xFF1A1A1A) : const Color(0xFFE8E8E8); + final mutedColor = light ? const Color(0xFF757575) : const Color(0xFF999999); + return ThemeData( brightness: brightness, useMaterial3: true, @@ -97,64 +97,107 @@ class AppTheme { fontFamily: effectiveFontFamily, textTheme: textTheme, fontFamilyFallback: const ['sans-serif', 'Roboto'], - scaffoldBackgroundColor: pitchBlackColor ?? (light ? Color.alphaBlend(effectivePrimary.withAlpha(10), Colors.white) : null), + scaffoldBackgroundColor: scaffoldBg, splashColor: Colors.transparent, - highlightColor: light ? Colors.black.withAlpha(20) : Colors.white.withAlpha(pitchBlackColor == null ? 10 : 25), - disabledColor: light ? const Color.fromARGB(200, 160, 160, 160) : const Color.fromARGB(200, 60, 60, 60), + splashFactory: NoSplash.splashFactory, + highlightColor: Colors.transparent, applyElevationOverlayColor: false, + dividerColor: borderColor, + dividerTheme: DividerThemeData( + thickness: 1, + color: borderColor, + space: 0, + ), appBarTheme: AppBarTheme( elevation: 0, scrolledUnderElevation: 0, surfaceTintColor: Colors.transparent, - backgroundColor: pitchBlackColor ?? (light ? Color.alphaBlend(effectivePrimary.withAlpha(25), Colors.white) : null), - actionsIconTheme: IconThemeData( - color: light ? const Color.fromARGB(200, 40, 40, 40) : const Color.fromARGB(200, 233, 233, 233), - ), - iconTheme: IconThemeData( - color: light ? const Color.fromARGB(200, 40, 40, 40) : const Color.fromARGB(200, 233, 233, 233), - ), + backgroundColor: scaffoldBg, + iconTheme: IconThemeData(color: textColor), + actionsIconTheme: IconThemeData(color: textColor), titleTextStyle: TextStyle( - color: light ? Colors.black.withAlpha(160) : Colors.white.withAlpha(210), - fontSize: 20, + color: textColor, + fontSize: 18, fontWeight: FontWeight.w600, fontFamily: effectiveFontFamily, ), ), - secondaryHeaderColor: light ? const Color.fromARGB(200, 240, 240, 240) : const Color.fromARGB(222, 10, 10, 10), - iconTheme: IconThemeData( - color: light ? const Color.fromARGB(200, 40, 40, 40) : const Color.fromARGB(200, 233, 233, 233), - ), - shadowColor: light ? const Color.fromARGB(180, 100, 100, 100) : const Color.fromARGB(222, 10, 10, 10), - dividerTheme: const DividerThemeData( - thickness: 4, - indent: 0.0, - endIndent: 0.0, - ), - cardColor: cardColor, + cardColor: cardBg, cardTheme: CardThemeData( - elevation: 12.0, - color: Color.alphaBlend( - getMainColorWithAlpha(45), - light ? const Color.fromARGB(255, 255, 255, 255) : pitchGrey, - ), + elevation: 0, + color: cardBg, + surfaceTintColor: Colors.transparent, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14.0 * 1.5), + borderRadius: BorderRadius.circular(10), + side: BorderSide(color: borderColor), ), ), dialogTheme: DialogThemeData( + elevation: 0, surfaceTintColor: Colors.transparent, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24.0 * 1.5)), - backgroundColor: light - ? Color.alphaBlend(getMainColorWithAlpha(60), Colors.white) - : Color.alphaBlend(getMainColorWithAlpha(20), pitchBlackColor ?? const Color.fromARGB(255, 12, 12, 12)), + backgroundColor: cardBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + side: BorderSide(color: borderColor), + ), ), popupMenuTheme: PopupMenuThemeData( + elevation: 0, surfaceTintColor: Colors.transparent, - elevation: 12.0, + color: cardBg, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide(color: borderColor), + ), + ), + bottomNavigationBarTheme: BottomNavigationBarThemeData( + elevation: 0, + backgroundColor: scaffoldBg, + selectedItemColor: textColor, + unselectedItemColor: mutedColor, + ), + navigationBarTheme: NavigationBarThemeData( + elevation: 0, + backgroundColor: scaffoldBg, + indicatorColor: borderColor, + labelTextStyle: WidgetStatePropertyAll(TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: textColor, + )), + iconTheme: WidgetStatePropertyAll(IconThemeData(color: mutedColor)), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: surfaceBg, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: borderColor), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: borderColor), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: colorScheme.primary, width: 1.5), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + isDense: true, + ), + floatingActionButtonTheme: FloatingActionButtonThemeData( + elevation: 0, + backgroundColor: cardBg, + foregroundColor: textColor, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0 * 1.5), + borderRadius: BorderRadius.circular(12), + side: BorderSide(color: borderColor), + ), + ), + iconButtonTheme: IconButtonThemeData( + style: IconButton.styleFrom( + foregroundColor: textColor, ), - color: light ? Color.alphaBlend(cardColor.withAlpha(180), Colors.white) : Color.alphaBlend(cardColor.withAlpha(180), Colors.black), ), ); } diff --git a/lib/main.dart b/lib/main.dart index 04c3853..7a85f3d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,8 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'core/theme.dart'; import 'core/icon_fonts/broken_icons.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'core/app_strings.dart'; import 'providers/file_manager_provider.dart'; import 'providers/media_provider.dart'; import 'services/preferences_service.dart'; @@ -32,6 +34,9 @@ void main() async { await NetworkConnectionsService.init(); await RecycleBinService.init(); + AppStrings.locale = PreferencesService.getLocale(); + await AppStrings.loadTranslations(); + // Load custom font dynamically if configured try { final customFontPath = PreferencesService.getCustomFontPath(); @@ -316,8 +321,14 @@ class _NFileAppState extends State { return MaterialApp( navigatorKey: navigatorKey, - title: 'NFile', + title: AppStrings.current.appTitle, debugShowCheckedModeBanner: false, + locale: PreferencesService.getLocale() == 'system' ? null : Locale(PreferencesService.getLocale()), + supportedLocales: AppStrings.supportedLocales, + localizationsDelegates: const [ + AppStrings.delegate, + ...GlobalMaterialLocalizations.delegates, + ], theme: AppTheme.getAppTheme(light: true, seed: baseSeedColor, customScheme: activeLightScheme, fontFamily: fileManager.fontFamilyOption), darkTheme: AppTheme.getAppTheme(light: false, pitchBlack: fileManager.amoledMode, seed: baseSeedColor, customScheme: activeDarkScheme, fontFamily: fileManager.fontFamilyOption), themeMode: activeThemeMode, @@ -418,7 +429,7 @@ class _IntentLoadingScreen extends StatelessWidget { ), const SizedBox(height: 20), Text( - 'Opening shared document...', + AppStrings.current.openingSharedDocument, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, color: theme.colorScheme.onSurface.withOpacity(0.8), @@ -426,7 +437,7 @@ class _IntentLoadingScreen extends StatelessWidget { ), const SizedBox(height: 6), Text( - 'Resolving secure content stream', + AppStrings.current.resolvingSecureContent, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.5), ), @@ -460,12 +471,12 @@ class _StoragePermissionShield extends StatelessWidget { ), const SizedBox(height: 24), Text( - 'Storage Access Required', + AppStrings.current.storageAccessRequired, style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Text( - 'NFile requires storage permission to manage, organize, and display your media files seamlessly.', + AppStrings.current.storagePermissionMessage, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey), ), @@ -477,7 +488,7 @@ class _StoragePermissionShield extends StatelessWidget { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), ), icon: const Icon(Broken.shield_tick), - label: const Text('Grant Permission', style: TextStyle(fontWeight: FontWeight.bold)), + label: Text(AppStrings.current.grantPermission, style: const TextStyle(fontWeight: FontWeight.bold)), ), ], ), diff --git a/lib/providers/file_manager_provider.dart b/lib/providers/file_manager_provider.dart index 2b51cdc..1676eec 100644 --- a/lib/providers/file_manager_provider.dart +++ b/lib/providers/file_manager_provider.dart @@ -21,6 +21,7 @@ import '../services/archive_service.dart'; import '../services/apk_installer_service.dart'; import '../ui/widgets/extract_archive_dialog.dart'; import '../core/utils.dart'; +import '../core/app_strings.dart'; import '../services/preferences_service.dart'; import '../services/app_manager_service.dart'; import '../models/custom_shortcut_model.dart'; @@ -90,7 +91,25 @@ int _calculateDirectorySizeSync(String path) { } class FileManagerProvider extends ChangeNotifier { + bool _isPickerMode = false; + bool get isPickerMode => _isPickerMode; + static const _pickerChannel = MethodChannel("com.rubex.nfile/picker"); + + Future checkPickerMode() async { + try { + _isPickerMode = await _pickerChannel.invokeMethod("isPickerMode") ?? false; + if (_isPickerMode) notifyListeners(); + } catch (e) {} + } + + Future returnPickerResult(String path) async { + try { + await _pickerChannel.invokeMethod("finishWithResult", {"path": path}); + } catch (e) {} + } + FileManagerProvider() { + checkPickerMode(); _sortType = PreferencesService.getSortType(); _isGridView = PreferencesService.getIsGridView(); _iconScale = PreferencesService.getIconScale(); @@ -153,7 +172,7 @@ class FileManagerProvider extends ChangeNotifier { if (_totalStorageBytes > 0) { _storageVolumes = [ StorageVolume( - name: 'Internal Storage', + name: AppStrings.current.uiInternalStorage, path: '/storage/emulated/0', isInternal: true, totalBytes: _totalStorageBytes, @@ -1120,7 +1139,7 @@ class FileManagerProvider extends ChangeNotifier { } } - if (filter == 'All' || filter == 'Audio') { + if (filter == 'All' || filter == AppStrings.current.uiAudio) { for (final song in mediaProvider.audios) { final path = song.data; if (!isGlobal && !path.startsWith(rootPath)) continue; @@ -1181,13 +1200,13 @@ class FileManagerProvider extends ChangeNotifier { bool matchFilter = false; if (filter == 'All') { matchFilter = true; - } else if (filter == 'Folders' && isDir) { + } else if (filter == AppStrings.current.uiFolders && isDir) { matchFilter = true; - } else if (filter == 'Images' && !isDir && isImage(name)) { + } else if (filter == AppStrings.current.uiImages && !isDir && isImage(name)) { matchFilter = true; - } else if (filter == 'Videos' && !isDir && isVideo(name)) { + } else if (filter == AppStrings.current.uiVideos && !isDir && isVideo(name)) { matchFilter = true; - } else if (filter == 'Audio' && !isDir && isAudio(name)) { + } else if (filter == AppStrings.current.uiAudio && !isDir && isAudio(name)) { matchFilter = true; } else if (filter == 'Docs' && !isDir && isDoc(name)) { matchFilter = true; @@ -1453,7 +1472,7 @@ class FileManagerProvider extends ChangeNotifier { Future _detectStorageVolumes() async { final volumes = []; if (Platform.isAndroid) { - volumes.add(StorageVolume(name: 'Internal Storage', path: '/storage/emulated/0', isInternal: true)); + volumes.add(StorageVolume(name: AppStrings.current.uiInternalStorage, path: '/storage/emulated/0', isInternal: true)); try { final extDirs = await getExternalStorageDirectories(); @@ -1492,7 +1511,7 @@ class FileManagerProvider extends ChangeNotifier { } catch (_) {} } else { final dir = await getApplicationDocumentsDirectory(); - volumes.add(StorageVolume(name: 'Documents', path: dir.path, isInternal: true)); + volumes.add(StorageVolume(name: AppStrings.current.uiDocuments, path: dir.path, isInternal: true)); } _storageVolumes = volumes; await updateStorageSpace(); @@ -1912,7 +1931,7 @@ class FileManagerProvider extends ChangeNotifier { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(_isCut ? 'Moved items successfully' : 'Copied items successfully'), + content: Text(_isCut ? AppStrings.current.movedItemsSuccessfully : AppStrings.current.copiedItemsSuccessfully), behavior: SnackBarBehavior.floating, ), ); @@ -1922,7 +1941,7 @@ class FileManagerProvider extends ChangeNotifier { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Failed to transfer: $e'), + content: Text(AppStrings.current.failedToTransfer(e.toString())), backgroundColor: Colors.redAccent, behavior: SnackBarBehavior.floating, ), @@ -1951,7 +1970,7 @@ class FileManagerProvider extends ChangeNotifier { if (context.mounted) { await FileActionDialogs.showWarningDialog( context, - title: 'Operation Cancelled', + title: AppStrings.current.uiOperationCancelled, content: 'Cannot cut and paste a file into the same folder.', ); } @@ -2316,7 +2335,7 @@ class FileManagerProvider extends ChangeNotifier { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Failed to connect to remote server: $e'), + content: Text(AppStrings.current.failedToConnectRemote(e.toString())), backgroundColor: Colors.redAccent, behavior: SnackBarBehavior.floating, ), @@ -2390,7 +2409,7 @@ class FileManagerProvider extends ChangeNotifier { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(_isCut ? 'Moved items successfully' : 'Copied items successfully'), + content: Text(_isCut ? AppStrings.current.movedItemsSuccessfully : AppStrings.current.copiedItemsSuccessfully), behavior: SnackBarBehavior.floating, ), ); @@ -2400,7 +2419,7 @@ class FileManagerProvider extends ChangeNotifier { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(e.toString().contains('Cancelled') ? 'Operation Cancelled' : 'Transfer failed: $e'), + content: Text(e.toString().contains('Cancelled') ? AppStrings.current.uiOperationCancelled : 'Transfer failed: $e'), backgroundColor: e.toString().contains('Cancelled') ? null : Colors.redAccent, behavior: SnackBarBehavior.floating, ), @@ -2673,7 +2692,7 @@ class FileManagerProvider extends ChangeNotifier { if (context != null && context.mounted) { await FileActionDialogs.showWarningDialog( context, - title: 'Compression Limit Exceeded', + title: AppStrings.current.uiCompressionLimitExceeded, content: 'TAR.ZSTD and TAR.LZ4 formats are highly memory-intensive and optimized for files under 600MB. Please use the ZIP or TAR format for larger files.', ); } @@ -2772,6 +2791,11 @@ class FileManagerProvider extends ChangeNotifier { } Future openFileNatively(BuildContext context, String path) async { + if (isPickerMode) { + await returnPickerResult(path); + return; + } + final mimeType = lookupMimeType(path) ?? ''; final ext = p.extension(path).toLowerCase(); const docExts = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.epub', '.odt']; @@ -2958,7 +2982,7 @@ class FileManagerProvider extends ChangeNotifier { if (sourcePath == destPath || destFolderPath.startsWith(sourcePath + p.separator)) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Cannot move a folder inside itself or same location')), + SnackBar(content: Text(AppStrings.current.cannotMoveIntoItself)), ); return; } @@ -3006,14 +3030,14 @@ class FileManagerProvider extends ChangeNotifier { if (showToast) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Moved $name successfully')), + SnackBar(content: Text(AppStrings.current.movedSuccessfully(name))), ); } } catch (e) { debugPrint('Error moving item: $e'); if (showToast) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to move item: $e')), + SnackBar(content: Text(AppStrings.current.failedToMove(e.toString()))), ); } } @@ -3039,7 +3063,7 @@ class FileManagerProvider extends ChangeNotifier { if (sourcePath == destPath || destFolderPath.startsWith(sourcePath + p.separator)) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Cannot copy a folder inside itself or same location')), + SnackBar(content: Text(AppStrings.current.cannotCopyIntoItself)), ); return; } @@ -3077,14 +3101,14 @@ class FileManagerProvider extends ChangeNotifier { if (showToast) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Copied $name successfully')), + SnackBar(content: Text(AppStrings.current.copedSuccessfully(name))), ); } } catch (e) { debugPrint('Error copying item: $e'); if (showToast) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to copy item: $e')), + SnackBar(content: Text(AppStrings.current.failedToCopy(e.toString()))), ); } } diff --git a/lib/providers/media_provider.dart b/lib/providers/media_provider.dart index 8b98ba3..142517d 100644 --- a/lib/providers/media_provider.dart +++ b/lib/providers/media_provider.dart @@ -14,6 +14,7 @@ import '../models/custom_shortcut_model.dart'; import '../models/file_item_model.dart'; import '../core/utils.dart'; +import '../core/app_strings.dart'; enum MediaSortOrder { newest, oldest, @@ -243,10 +244,10 @@ class MediaProvider extends ChangeNotifier { List get videoAlbums => _videoAlbums; List _categoryOrder = [ - 'Images', - 'Videos', - 'Audio', - 'Documents', + AppStrings.current.uiImages, + AppStrings.current.uiVideos, + AppStrings.current.uiAudio, + AppStrings.current.uiDocuments, 'Archives', 'Downloads', 'APKs', @@ -257,10 +258,10 @@ class MediaProvider extends ChangeNotifier { ]; List _activeCategories = [ - 'Images', - 'Videos', - 'Audio', - 'Documents', + AppStrings.current.uiImages, + AppStrings.current.uiVideos, + AppStrings.current.uiAudio, + AppStrings.current.uiDocuments, 'Archives', 'Downloads', 'APKs', @@ -301,7 +302,7 @@ class MediaProvider extends ChangeNotifier { } List get images { - final excluded = _excludedDefaultPaths['Images'] ?? []; + final excluded = _excludedDefaultPaths[AppStrings.current.uiImages] ?? []; final excludeGallery = excluded.contains('Device Gallery (Auto)'); final list = [..._images, ..._customImages].where((item) { if (item is AssetEntity && excludeGallery) return false; @@ -316,7 +317,7 @@ class MediaProvider extends ChangeNotifier { } List get videos { - final excluded = _excludedDefaultPaths['Videos'] ?? []; + final excluded = _excludedDefaultPaths[AppStrings.current.uiVideos] ?? []; final excludeGallery = excluded.contains('Device Gallery (Auto)'); final list = [..._videos, ..._customVideos].where((item) { if (item is AssetEntity && excludeGallery) return false; @@ -331,7 +332,7 @@ class MediaProvider extends ChangeNotifier { } List get audios { - final excluded = _excludedDefaultPaths['Audio'] ?? []; + final excluded = _excludedDefaultPaths[AppStrings.current.uiAudio] ?? []; final excludeLibrary = excluded.contains('Device Audio Library (Auto)'); return _audios.where((song) { if (excludeLibrary && song.id < 900000) return false; @@ -342,10 +343,10 @@ class MediaProvider extends ChangeNotifier { } List get documents { - final excluded = _excludedDefaultPaths['Documents'] ?? []; + final excluded = _excludedDefaultPaths[AppStrings.current.uiDocuments] ?? []; final excludeAllScanned = excluded.contains('Internal Storage (All Folders Scanned)'); return _documents.where((file) { - final docPaths = _customCategoryPaths['Documents'] ?? []; + final docPaths = _customCategoryPaths[AppStrings.current.uiDocuments] ?? []; final isCustom = docPaths.any((dir) => p.isWithin(dir, file.path)); if (excludeAllScanned && !isCustom) return false; if (_isPathExcluded(file.path, excluded)) return false; @@ -518,19 +519,19 @@ class MediaProvider extends ChangeNotifier { int getCategoryItemCount(String category) { if (_isLoaded) { - switch (category) { - case 'Images': return images.length; - case 'Videos': return videos.length; - case 'Audio': return _audios.length; - case 'Documents': return _documents.length; - case 'Archives': return _archives.length; - case 'Downloads': return _downloads.length; - case 'APKs': return _apks.length; - case 'Screenshots': return screenshots.length; - case 'Apps': return 0; - case 'Settings': return 0; - } + if (category == AppStrings.current.uiImages) return images.length; + else if (category == AppStrings.current.uiVideos) return videos.length; + else if (category == AppStrings.current.uiAudio) return _audios.length; + else if (category == AppStrings.current.uiDocuments) return _documents.length; + else if (category == 'Archives') return _archives.length; + else if (category == 'Downloads') return _downloads.length; + else if (category == 'APKs') return _apks.length; + else if (category == 'Screenshots') return screenshots.length; + else if (category == 'Apps') return 0; + else if (category == 'Settings') return 0; + else return 0; } + return PreferencesService.getCategoryCount(category); } @@ -792,10 +793,10 @@ class MediaProvider extends ChangeNotifier { await _saveCache(); _applySort(); - PreferencesService.saveCategoryCount('Images', images.length); - PreferencesService.saveCategoryCount('Videos', videos.length); - PreferencesService.saveCategoryCount('Audio', _audios.length); - PreferencesService.saveCategoryCount('Documents', _documents.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiImages, images.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiVideos, videos.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiAudio, _audios.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiDocuments, _documents.length); PreferencesService.saveCategoryCount('Archives', _archives.length); PreferencesService.saveCategoryCount('Downloads', _downloads.length); PreferencesService.saveCategoryCount('APKs', _apks.length); @@ -918,10 +919,10 @@ class MediaProvider extends ChangeNotifier { _applySort(); - PreferencesService.saveCategoryCount('Images', images.length); - PreferencesService.saveCategoryCount('Videos', videos.length); - PreferencesService.saveCategoryCount('Audio', _audios.length); - PreferencesService.saveCategoryCount('Documents', _documents.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiImages, images.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiVideos, videos.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiAudio, _audios.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiDocuments, _documents.length); PreferencesService.saveCategoryCount('Archives', _archives.length); PreferencesService.saveCategoryCount('Downloads', _downloads.length); PreferencesService.saveCategoryCount('APKs', _apks.length); @@ -1087,7 +1088,7 @@ class MediaProvider extends ChangeNotifier { Future _loadDocuments() async { final docs = []; final searchDirs = await _getUserSearchDirs(); - final excluded = _excludedDefaultPaths['Documents'] ?? []; + final excluded = _excludedDefaultPaths[AppStrings.current.uiDocuments] ?? []; for (final dirPath in searchDirs) { if (_isPathExcluded(dirPath, excluded)) continue; @@ -1098,7 +1099,7 @@ class MediaProvider extends ChangeNotifier { ); } - final docPaths = _customCategoryPaths['Documents'] ?? []; + final docPaths = _customCategoryPaths[AppStrings.current.uiDocuments] ?? []; for (final dirPath in docPaths) { if (await Directory(dirPath).exists()) { await _scanDirectoryRecursively( @@ -1204,16 +1205,16 @@ class MediaProvider extends ChangeNotifier { } Future _scanCustomCategories() async { - final imagePaths = _customCategoryPaths['Images'] ?? []; + final imagePaths = _customCategoryPaths[AppStrings.current.uiImages] ?? []; _customImages = await _scanCustomPaths(imagePaths, FileUtils.isImage); - final videoPaths = _customCategoryPaths['Videos'] ?? []; + final videoPaths = _customCategoryPaths[AppStrings.current.uiVideos] ?? []; _customVideos = await _scanCustomPaths(videoPaths, FileUtils.isVideo); final screenshotPaths = _customCategoryPaths['Screenshots'] ?? []; _customScreenshots = await _scanCustomPaths(screenshotPaths, FileUtils.isImage); - final audioPaths = _customCategoryPaths['Audio'] ?? []; + final audioPaths = _customCategoryPaths[AppStrings.current.uiAudio] ?? []; final customAudFiles = await _scanCustomPaths(audioPaths, FileUtils.isAudio); _audios.removeWhere((song) => song.id >= 900000); final existingAudioPaths = _audios.map((s) => s.data).toSet(); @@ -1240,7 +1241,7 @@ class MediaProvider extends ChangeNotifier { } // Documents custom path scan and merge - final docPaths = _customCategoryPaths['Documents'] ?? []; + final docPaths = _customCategoryPaths[AppStrings.current.uiDocuments] ?? []; final customDocs = await _scanCustomPaths(docPaths, (ext) => _docExtensions.contains(ext)); _documents.removeWhere((entity) { final isInCustomPath = docPaths.any((dir) => p.isWithin(dir, entity.path)); @@ -1599,10 +1600,10 @@ class MediaProvider extends ChangeNotifier { } // Update Counts and Cache - PreferencesService.saveCategoryCount('Images', images.length); - PreferencesService.saveCategoryCount('Videos', videos.length); - PreferencesService.saveCategoryCount('Audio', _audios.length); - PreferencesService.saveCategoryCount('Documents', _documents.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiImages, images.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiVideos, videos.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiAudio, _audios.length); + PreferencesService.saveCategoryCount(AppStrings.current.uiDocuments, _documents.length); PreferencesService.saveCategoryCount('Archives', _archives.length); PreferencesService.saveCategoryCount('Downloads', _downloads.length); PreferencesService.saveCategoryCount('APKs', _apks.length); diff --git a/lib/services/apk_installer_service.dart b/lib/services/apk_installer_service.dart index e246fb0..280de2f 100644 --- a/lib/services/apk_installer_service.dart +++ b/lib/services/apk_installer_service.dart @@ -5,6 +5,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:open_filex/open_filex.dart'; import 'archive_service.dart'; import 'app_manager_service.dart'; +import '../core/app_strings.dart'; class ApkInstallerService { static const List apkExtensions = ['.apk', '.xapk', '.apks', '.apkm', '.aab']; @@ -26,12 +27,12 @@ class ApkInstallerService { showDialog( context: context, barrierDismissible: false, - builder: (_) => const AlertDialog( + builder: (_) => AlertDialog( content: Row( children: [ CircularProgressIndicator(), SizedBox(width: 20), - Expanded(child: Text("Extracting package bundle for installation...")), + Expanded(child: Text(AppStrings.current.extractingBundle)), ], ), ), @@ -78,7 +79,7 @@ class ApkInstallerService { if (!context.mounted) return; Navigator.pop(context); // Close loading dialog ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('No installable APK found in package bundle')), + SnackBar(content: Text(AppStrings.current.noInstallableApk)), ); return; } @@ -94,7 +95,7 @@ class ApkInstallerService { final success = await AppManagerService.installSplitApks(apkPaths); if (!success && context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Failed to trigger split APK installer')), + SnackBar(content: Text(AppStrings.current.failedToTriggerInstaller)), ); } } @@ -102,7 +103,7 @@ class ApkInstallerService { if (!context.mounted) return; Navigator.pop(context); // Close loading dialog ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to extract package bundle: $e')), + SnackBar(content: Text(AppStrings.current.failedToExtractBundle(e.toString()))), ); } } diff --git a/lib/services/audio_background_handler.dart b/lib/services/audio_background_handler.dart index 6e95704..79d840f 100644 --- a/lib/services/audio_background_handler.dart +++ b/lib/services/audio_background_handler.dart @@ -5,6 +5,7 @@ import 'package:media_kit/media_kit.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter/foundation.dart'; import '../ui/screens/audio_player/audio_artwork_widget.dart'; +import '../core/app_strings.dart'; /// Global singleton handler instance NFileAudioHandler? _audioHandlerInstance; @@ -221,9 +222,9 @@ class NFileAudioHandler extends BaseAudioHandler MediaControl.skipToPrevious, playing ? MediaControl.pause : MediaControl.play, MediaControl.skipToNext, - const MediaControl( + MediaControl( androidIcon: 'drawable/ic_close', - label: 'Close', + label: AppStrings.current.close, action: MediaAction.stop, ), ], diff --git a/lib/services/background_archive_service.dart b/lib/services/background_archive_service.dart index bffbe58..63950ad 100644 --- a/lib/services/background_archive_service.dart +++ b/lib/services/background_archive_service.dart @@ -11,6 +11,7 @@ import '../providers/file_manager_provider.dart'; import 'package:provider/provider.dart'; import '../ui/widgets/background_operation_progress_dialog.dart'; +import '../core/app_strings.dart'; class BackgroundOperation { final String id; final String title; @@ -65,7 +66,7 @@ class BackgroundArchiveService { final archiveName = p.basename(destinationPath); final operation = BackgroundOperation( id: 'compress_${DateTime.now().millisecondsSinceEpoch}', - title: 'Compressing Files', + title: AppStrings.current.uiCompressingFiles, archiveName: archiveName, isCompression: true, ); @@ -124,7 +125,7 @@ class BackgroundArchiveService { final archiveName = p.basename(archivePath); final operation = BackgroundOperation( id: 'extract_${DateTime.now().millisecondsSinceEpoch}', - title: 'Extracting Archive', + title: AppStrings.current.uiExtractingArchive, archiveName: archiveName, isCompression: false, ); diff --git a/lib/services/folder_share_service.dart b/lib/services/folder_share_service.dart index ae7f3b7..4fe7c97 100644 --- a/lib/services/folder_share_service.dart +++ b/lib/services/folder_share_service.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:path/path.dart' as p; import 'package:share_plus/share_plus.dart'; import 'archive_service.dart'; +import '../core/app_strings.dart'; class FolderShareService { /// Compresses folders into temporary ZIP archives and shares files + compressed folders natively. @@ -13,7 +14,7 @@ class FolderShareService { showDialog( context: context, barrierDismissible: false, - builder: (ctx) => const Center( + builder: (ctx) => Center( child: Card( child: Padding( padding: EdgeInsets.all(24.0), @@ -23,12 +24,12 @@ class FolderShareService { CircularProgressIndicator(), SizedBox(height: 16), Text( - 'Preparing folders for sharing...', + AppStrings.current.uiPreparingFoldersForSharing, style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text( - 'Compressing contents, please wait', + AppStrings.current.uiCompressingContentsPleaseWait, style: TextStyle(fontSize: 12, color: Colors.grey), ), ], @@ -81,7 +82,7 @@ class FolderShareService { } else { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('No shareable items found.')), + SnackBar(content: Text(AppStrings.current.noShareableItems)), ); } } @@ -90,7 +91,7 @@ class FolderShareService { if (context.mounted) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Error preparing files to share: $e')), + SnackBar(content: Text(AppStrings.current.errorPreparingFiles(e.toString()))), ); } } finally { diff --git a/lib/services/intent_handler_service.dart b/lib/services/intent_handler_service.dart index c36dafa..e8e5d07 100644 --- a/lib/services/intent_handler_service.dart +++ b/lib/services/intent_handler_service.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../providers/file_manager_provider.dart'; +import '../core/app_strings.dart'; class IntentHandlerService { static const MethodChannel _channel = MethodChannel('com.rubex.nfile/root_shizuku'); @@ -38,7 +39,7 @@ class IntentHandlerService { debugPrint('Error resolving content URI: $e'); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Error reading shared file: $e')), + SnackBar(content: Text(AppStrings.current.errorReadingSharedFile(e.toString()))), ); } return; diff --git a/lib/services/preferences_service.dart b/lib/services/preferences_service.dart index 37bab28..a847e39 100644 --- a/lib/services/preferences_service.dart +++ b/lib/services/preferences_service.dart @@ -633,4 +633,14 @@ class PreferencesService { static Future saveExitOption(String val) async { await _prefs?.setString(_keyExitOption, val); } + + static const String _keyLocale = 'locale'; + + static String getLocale() { + return _prefs?.getString(_keyLocale) ?? 'system'; + } + + static Future saveLocale(String locale) async { + await _prefs?.setString(_keyLocale, locale); + } } diff --git a/lib/services/remote/saf_client.dart b/lib/services/remote/saf_client.dart index e682653..c8835f1 100644 --- a/lib/services/remote/saf_client.dart +++ b/lib/services/remote/saf_client.dart @@ -1,6 +1,7 @@ import 'package:flutter/services.dart'; import 'remote_client.dart'; +import '../../core/app_strings.dart'; class SafRemoteClient implements RemoteClient { final String rootUri; static const _channel = MethodChannel('com.rubex.nfile/saf'); @@ -41,7 +42,7 @@ class SafRemoteClient implements RemoteClient { Future createDirectory(String path) async { final int lastSlash = path.lastIndexOf('/'); final String parentUri = lastSlash != -1 ? path.substring(0, lastSlash) : ''; - final String folderName = lastSlash != -1 ? path.substring(lastSlash + 1) : 'New Folder'; + final String folderName = lastSlash != -1 ? path.substring(lastSlash + 1) : AppStrings.current.newFolder; await _channel.invokeMethod('createDirectory', { 'rootUri': rootUri, diff --git a/lib/services/root_shizuku_service.dart b/lib/services/root_shizuku_service.dart index e2dc9eb..f175daf 100644 --- a/lib/services/root_shizuku_service.dart +++ b/lib/services/root_shizuku_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/services.dart'; import '../models/file_item_model.dart'; import 'package:path/path.dart' as p; +import '../core/app_strings.dart'; class RootShizukuStatus { final bool isRootAvailable; @@ -84,6 +85,9 @@ class RootShizukuService { }); return res?.toString(); } catch (e) { + if (e.toString().contains('EXEC_ERROR')) { + throw Exception(AppStrings.current.errorShizukuNotRunning); + } throw Exception('Execution failed: $e'); } } diff --git a/lib/services/settings_backup_service.dart b/lib/services/settings_backup_service.dart index 80cc0d1..92e11e6 100644 --- a/lib/services/settings_backup_service.dart +++ b/lib/services/settings_backup_service.dart @@ -5,6 +5,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:provider/provider.dart'; import '../providers/file_manager_provider.dart'; import '../providers/media_provider.dart'; +import '../core/app_strings.dart'; class SettingsBackupService { static const String _backupDir = '/storage/emulated/0/NFile/Backups/Settings'; @@ -43,8 +44,8 @@ class SettingsBackupService { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Settings backed up to NFile/Backups/Settings/nfile_settings_backup.json'), + SnackBar( + content: Text(AppStrings.current.settingsBackedUp), behavior: SnackBarBehavior.floating, ), ); @@ -53,7 +54,7 @@ class SettingsBackupService { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Failed to backup settings: $e'), + content: Text(AppStrings.current.failedToBackup(e.toString())), behavior: SnackBarBehavior.floating, backgroundColor: Theme.of(context).colorScheme.error, ), @@ -108,8 +109,8 @@ class SettingsBackupService { Provider.of(context, listen: false).reloadPreferences(); Provider.of(context, listen: false).reloadPreferences(); ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Settings restored successfully!'), + SnackBar( + content: Text(AppStrings.current.settingsRestored), behavior: SnackBarBehavior.floating, ), ); @@ -118,7 +119,7 @@ class SettingsBackupService { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Failed to restore settings: $e'), + content: Text(AppStrings.current.failedToRestore(e.toString())), behavior: SnackBarBehavior.floating, backgroundColor: Theme.of(context).colorScheme.error, ), diff --git a/lib/services/web_sharing_service.dart b/lib/services/web_sharing_service.dart index 7e9c67d..98fd146 100644 --- a/lib/services/web_sharing_service.dart +++ b/lib/services/web_sharing_service.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:path/path.dart' as p; import 'package:dartssh2/dartssh2.dart'; +import '../core/app_strings.dart'; class WebSharingService extends ChangeNotifier { static final WebSharingService instance = WebSharingService._(); WebSharingService._(); @@ -332,8 +333,8 @@ AAAEBbg6hQHydFb0ZGHuYq+gCui5fFtXW1X2e3Ok3UKTfXMhY3eZl04qtec/5UVUNLrK49
$backSvg
-
.. (Parent Directory)
-
Go up one level
+
${AppStrings.current.webParentDir}
+
${AppStrings.current.webGoUpLevel}
'''; @@ -406,7 +407,7 @@ AAAEBbg6hQHydFb0ZGHuYq+gCui5fFtXW1X2e3Ok3UKTfXMhY3eZl04qtec/5UVUNLrK49 // Files render with clean hover download actions and explicit item metadata details final actionsHtml = '''
-
@@ -431,10 +432,10 @@ AAAEBbg6hQHydFb0ZGHuYq+gCui5fFtXW1X2e3Ok3UKTfXMhY3eZl04qtec/5UVUNLrK49 final badgeHtml = '''
- -
'''; @@ -446,18 +447,18 @@ AAAEBbg6hQHydFb0ZGHuYq+gCui5fFtXW1X2e3Ok3UKTfXMhY3eZl04qtec/5UVUNLrK49 NFile Shared Portal - $title - +