From 33a154a5d7502419381dc601aff1e4b0c660054d Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Fri, 30 Oct 2026 16:06:37 +0200 Subject: [PATCH 1/9] Fix F-Droid 1.5.0 build by disabling lockfile enforcement (#16673) * build_fdroid.sh: Revert unnecessary sed un-bracing Claude has identified the issue but optimized stuff blindly: the fact that F-Droid recipe for RustDesk 1.4.9 abused the sed instruction does not mean it will be abused continuously, nor that it should be abused (and fixed). Signed-off-by: Vasyl Gello * build_fdroid.sh: do not attempt applying dropdown patch on 3.44.0+ Signed-off-by: Vasyl Gello * Disable lockfile enforcement for now Signed-off-by: Vasyl Gello --------- Signed-off-by: Vasyl Gello --- flutter/build_fdroid.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index cc40e1422..1f3e027d3 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -40,7 +40,7 @@ prepare_flutter() { # Patch flutter - if dpkg --compare-versions "${VERSION}" ge "3.24.4"; then + if dpkg --compare-versions "${VERSION}" ge "3.24.4" && dpkg --compare-versions "${VERSION}" lt "3.44.0"; then git apply "${ROOTDIR}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff" fi @@ -324,11 +324,7 @@ prebuild) # Install Flutter bridge version - # Unbraced on purpose: F-Droid recipes rewrite the braced form with sed to - # pin a version, but the bridge generator only works with the Flutter - # version bridge.yml uses - - prepare_flutter "$FLUTTER_BRIDGE_VERSION" "${HOME}/flutter" + prepare_flutter "${FLUTTER_BRIDGE_VERSION}" "${HOME}/flutter" # Save changes @@ -346,7 +342,7 @@ prebuild) pushd flutter flutter clean - flutter packages pub get --enforce-lockfile + flutter packages pub get popd # flutter From 0d6265bb26b8127061ea14c7a5e5c602ddec8778 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 31 Oct 2026 10:11:24 +0800 Subject: [PATCH 2/9] fix(ios): adopt the UIScene life cycle (#16776) * fix(ios): adopt the UIScene life cycle UIKit refuses to launch an app linked against the iOS 27 SDK that has not adopted the UIScene life cycle: it traps in _UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption with "UIScene life cycle is required for apps built with this SDK". 1.5.1 (69) is the first build made with Xcode 27.1, so it crashes at launch on every iOS 27 device. Flutter 3.24.5 has no FlutterSceneDelegate, so add a minimal one: - Info.plist declares a single-scene manifest that loads Main.storyboard. - SceneDelegate hands the storyboard window to AppDelegate when the scene connects. FlutterAppDelegate finds the engine for plugin registration through its window, which no longer exists in didFinishLaunching, so registration moves there. - The launch URL now arrives in the scene connection options and later URLs in scene(_:openURLContexts:). Forward both to plugins through the app delegate callbacks uni_links listens on, so rustdesk:// links keep working on cold and warm start. Verified on the iOS 27.1 simulator with a Flutter 3.24.5 app that has the same Runner setup: the app launches, plugin channels respond, the initial link and stream links arrive, and lifecycle states reach Dart. Fixes #16766 Co-Authored-By: Claude Opus 5.5 * fix(ios): drive the status bar from the view controller under UIScene With UIViewControllerBasedStatusBarAppearance=false, Flutter 3.24.5 hides and styles the status bar through the app-level UIApplication setters. UIKit ignores those once the app uses the UIScene life cycle, so SystemChrome.setEnabledSystemUIMode(manual, overlays: []) no longer hides the status bar during a remote session. Set the key to true so Flutter goes through FlutterViewController's prefersStatusBarHidden / preferredStatusBarStyle instead. Checked on the iOS 27.1 simulator in scene mode: hiding, showing again and the light overlay style all take effect. The Dart side only uses SystemChrome, so it does not depend on this key. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- flutter/ios/Runner.xcodeproj/project.pbxproj | 4 ++++ flutter/ios/Runner/AppDelegate.swift | 14 +++++++++++- flutter/ios/Runner/Info.plist | 23 +++++++++++++++++++- flutter/ios/Runner/SceneDelegate.swift | 21 ++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 flutter/ios/Runner/SceneDelegate.swift diff --git a/flutter/ios/Runner.xcodeproj/project.pbxproj b/flutter/ios/Runner.xcodeproj/project.pbxproj index 0a6c9e459..edf2b9f39 100644 --- a/flutter/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 5CE7AD012EB4000000A1B2C3 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE7AD002EB4000000A1B2C3 /* SceneDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -40,6 +41,7 @@ 4151ACC476A12FDC49BC7860 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 5CE7AD002EB4000000A1B2C3 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -104,6 +106,7 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 5CE7AD002EB4000000A1B2C3 /* SceneDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; @@ -279,6 +282,7 @@ buildActionMask = 2147483647; files = ( 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 5CE7AD012EB4000000A1B2C3 /* SceneDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/flutter/ios/Runner/AppDelegate.swift b/flutter/ios/Runner/AppDelegate.swift index 1c9e71b50..5d177b2db 100644 --- a/flutter/ios/Runner/AppDelegate.swift +++ b/flutter/ios/Runner/AppDelegate.swift @@ -8,7 +8,6 @@ import AVFoundation _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) do { // Mix remote audio with other apps while still respecting silent mode. try AVAudioSession.sharedInstance().setCategory(.ambient) @@ -18,6 +17,19 @@ import AVFoundation dummyMethodToEnforceBundling(); return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + // With the UIScene life cycle, the FlutterViewController only exists once the scene + // connects, and FlutterAppDelegate reaches its engine through `window`. A launch URL + // also arrives with the scene instead of in launchOptions, so forward it to plugins here. + func sceneWillConnect(window: UIWindow?, launchURL: URL?) { + self.window = window + GeneratedPluginRegistrant.register(with: self) + var launchOptions: [UIApplication.LaunchOptionsKey: Any] = [:] + if let launchURL = launchURL { + launchOptions[.url] = launchURL + } + _ = super.application(UIApplication.shared, didFinishLaunchingWithOptions: launchOptions) + } public func dummyMethodToEnforceBundling() { dummy_method_to_enforce_bundling(); diff --git a/flutter/ios/Runner/Info.plist b/flutter/ios/Runner/Info.plist index 2d655a922..78fd51dd0 100644 --- a/flutter/ios/Runner/Info.plist +++ b/flutter/ios/Runner/Info.plist @@ -47,6 +47,27 @@ This app needs camera access to scan QR codes NSPhotoLibraryUsageDescription This app needs photo library access to get QR codes from image + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + UIApplicationSupportsIndirectInputEvents UIFileSharingEnabled @@ -71,7 +92,7 @@ UISupportsDocumentBrowser UIViewControllerBasedStatusBarAppearance - + io.flutter.embedded_views_preview diff --git a/flutter/ios/Runner/SceneDelegate.swift b/flutter/ios/Runner/SceneDelegate.swift new file mode 100644 index 000000000..811717650 --- /dev/null +++ b/flutter/ios/Runner/SceneDelegate.swift @@ -0,0 +1,21 @@ +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + (UIApplication.shared.delegate as? AppDelegate)?.sceneWillConnect( + window: window, launchURL: connectionOptions.urlContexts.first?.url) + } + + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + for context in URLContexts { + _ = appDelegate.application(UIApplication.shared, open: context.url, options: [:]) + } + } +} From eab27cc249b217f8662a7dac0c7d974501173303 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 31 Oct 2026 15:40:32 +0800 Subject: [PATCH 3/9] Build Windows custom client packers in CI (#16781) * Build Windows custom client packers in CI Verify payload signatures, build templates for all three Windows architectures, and include the packers in the unsigned release archive. Signed-off-by: fufesou * fix(ci): preserve unsigned Windows releases without signing Signed-off-by: fufesou * fix(ci): align template packer DPI manifests Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 89 ++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index f9dfee5c6..4850efd7b 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -426,6 +426,37 @@ jobs: throw "RustDesk reports version '$reportedVersion', expected '$env:VERSION'" } + - name: Verify template payload signatures + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + shell: powershell + run: | + Get-ChildItem ./rustdesk -Recurse -File -ErrorAction Stop | + Where-Object { $_.Extension -eq '.exe' -or ($_.Extension -eq '.dll' -and $_.FullName -notmatch 'RustDeskPrinterDriver|usbmmidd_v2') } | + ForEach-Object { + $signature = Get-AuthenticodeSignature -LiteralPath $_.FullName -ErrorAction Stop + if ($signature.Status -ne 'Valid') { throw "Invalid template payload signature: $($_.FullName) ($($signature.Status))" } + } + + - name: Build template packer + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + shell: bash + run: | + sed -i '/dpiAware/d' res/manifest.xml + pushd ./libs/portable + pip3 install -r requirements.txt + python3 ./generate.py -f ../../rustdesk/ -e rustdesk.exe -l 11 -t "${{ matrix.job.target }}" --exclude-exe + popd + mkdir -p ./packer-template + cp ./target/${{ matrix.job.target }}/release/rustdesk-portable-packer.exe ./packer-template/rustdesk-template-${{ matrix.job.arch }}.exe + + - name: Upload unsigned template packer + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: rustdesk-unsigned-packer-template-${{ matrix.job.arch }} + path: ./packer-template + if-no-files-found: error + - name: Build self-extracted executable shell: bash if: env.UPLOAD_ARTIFACT == 'true' @@ -690,6 +721,37 @@ jobs: throw "RustDesk reports version '$reportedVersion', expected '$env:VERSION'" } + - name: Verify template payload signatures + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + shell: powershell + run: | + Get-ChildItem ./Release -Recurse -File -ErrorAction Stop | + Where-Object { $_.Extension -eq '.exe' -or ($_.Extension -eq '.dll' -and $_.FullName -notmatch 'RustDeskPrinterDriver|usbmmidd_v2') } | + ForEach-Object { + $signature = Get-AuthenticodeSignature -LiteralPath $_.FullName -ErrorAction Stop + if ($signature.Status -ne 'Valid') { throw "Invalid template payload signature: $($_.FullName) ($($signature.Status))" } + } + + - name: Build template packer + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + shell: bash + run: | + sed -i '/dpiAware/d' res/manifest.xml + pushd ./libs/portable + pip3 install -r requirements.txt + python3 ./generate.py -f ../../Release/ -e rustdesk.exe -l 11 -t "${{ matrix.job.target }}" + popd + mkdir -p ./packer-template + cp ./target/${{ matrix.job.target }}/release/rustdesk-portable-packer.exe ./packer-template/rustdesk-template-${{ matrix.job.arch }}.exe + + - name: Upload unsigned template packer + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: rustdesk-unsigned-packer-template-${{ matrix.job.arch }} + path: ./packer-template + if-no-files-found: error + - name: Build self-extracted executable shell: bash run: | @@ -1301,9 +1363,34 @@ jobs: name: rustdesk-unsigned-msi-template-aarch64 path: ./msi-template/ + - name: Download Artifacts + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: rustdesk-unsigned-packer-template-x86_64 + path: ./packer-template/ + + - name: Download Artifacts + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: rustdesk-unsigned-packer-template-aarch64 + path: ./packer-template/ + + - name: Download Artifacts + if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: rustdesk-unsigned-packer-template-x86 + path: ./packer-template/ + - name: Combine unsigned app run: | - tar czf rustdesk-${{ env.VERSION }}-unsigned.tar.gz *.dmg windows-x86_64 windows-aarch64 windows-x86 msi-template + packer_template=() + if [[ "$SIGN_BASE_URL" != '-2' ]]; then + packer_template=(packer-template) + fi + tar czf rustdesk-${{ env.VERSION }}-unsigned.tar.gz *.dmg windows-x86_64 windows-aarch64 windows-x86 msi-template "${packer_template[@]}" - name: Publish unsigned app uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 From 3b7e2cebd9303a43eecb9bc96921548df846f45d Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 31 Oct 2026 19:59:06 +0800 Subject: [PATCH 4/9] fix: restore readiness after service restart and macOS update (#16780) * fix: restore readiness after service restart and macOS update Wake rendezvous retry waits when a manual restart is requested. For app-only macOS updates with an in-process backend, wait for the old process to exit before relaunching the updated app so it cannot attach to the departing backend. Signed-off-by: fufesou * test: cover service restart and macOS update readiness Signed-off-by: fufesou * test: remove macOS relaunch script regression test Signed-off-by: fufesou * docs: explain macOS update relaunch ordering Signed-off-by: fufesou * fix: isolate macOS update relaunch process group Signed-off-by: fufesou * docs: clarify macOS relaunch process group isolation Signed-off-by: fufesou * fix: clarify macOS update restart failure Signed-off-by: fufesou --------- Signed-off-by: fufesou --- src/platform/macos.rs | 43 ++++++++++++++++++++++++++++++++++++++ src/rendezvous_mediator.rs | 41 ++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/platform/macos.rs b/src/platform/macos.rs index ddc190ccc..d321d12f5 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -1015,6 +1015,11 @@ fn update_me_from_source(update_source: UpdateSource) -> ResultType<()> { } Ok(output) => log_update_cleanup_warning(&output), } + if crate::common::is_server_running() { + // This process hosts the backend. A new GUI could reuse our IPC and + // skip starting its own backend, leaving it "Not ready" when we exit. + return relaunch_after_update_exit(&app_name); + } } match Command::new("open") .arg("-n") @@ -1034,6 +1039,44 @@ fn update_me_from_source(update_source: UpdateSource) -> ResultType<()> { Ok(()) } +fn relaunch_after_update_exit(app_name: &str) -> ResultType<()> { + // The update caller cleans up and quits this GUI after we return. Waiting + // in a separate process lets that caller finish instead of blocking its exit. + // The shell's kill -0 only checks whether our PID exists; it does not kill us. + const RELAUNCH: &str = r#" +poll_interval=0.1 +while /bin/kill -0 "$1" 2>/dev/null; do + /bin/sleep "$poll_interval" +done +if ! output=$(/usr/bin/open -n "$2" 2>&1); then + /usr/bin/logger -t "$0" "Failed to relaunch updated app: $output" + exit 1 +fi +"#; + Command::new("/bin/sh") + .args(["-c", RELAUNCH, app_name]) + .arg(std::process::id().to_string()) + .arg(format!("/Applications/{}.app", app_name)) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + // Children normally inherit their parent's process group, allowing a + // supervisor to signal the whole group. If launchd cleans up the old + // app's group on exit, it could kill this helper before it runs `open`. + // Passing 0 creates a new group whose ID is the child's own PID, so + // cleanup of the old group does not affect the helper or its children. + // It remains our child; ordinary parent exit alone does not kill it. + .process_group(0) + .spawn() + .map_err(|err| { + anyhow!( + "The new version is installed, but automatic restart failed. Please quit and reopen the application. Error: {}", + err + ) + })?; + Ok(()) +} + pub fn update_from_dmg(dmg_path: &str) -> ResultType<()> { let update_temp_dir = get_update_temp_dir_string(); println!("Starting update from DMG: {}", dmg_path); diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 042d62b6c..b1e523da3 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -23,7 +23,7 @@ use hbb_common::{ socket_client::{self, connect_tcp, is_ipv4, new_direct_udp_for, new_udp_for}, tokio::{ self, select, - sync::{mpsc, Mutex}, + sync::{mpsc, Mutex, Notify}, time::interval, }, udp::FramedSocket, @@ -208,6 +208,7 @@ impl IceRoute { static SHOULD_EXIT: AtomicBool = AtomicBool::new(false); static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false); +static MANUAL_RESTART_NOTIFY: Notify = Notify::const_new(); static SENT_REGISTER_PK: AtomicBool = AtomicBool::new(false); pub(crate) static NEEDS_DEPLOY: AtomicBool = AtomicBool::new(false); #[cfg(target_os = "android")] @@ -256,6 +257,13 @@ pub(crate) fn reset_needs_deploy_notification() { NOTIFIED_NEEDS_DEPLOY.store(false, Ordering::SeqCst); } +async fn wait_for_retry(delay: f32, restarted: tokio::sync::futures::Notified<'_>) { + select! { + _ = sleep(delay) => {} + _ = restarted => {} + } +} + #[derive(Clone)] pub struct RendezvousMediator { addr: TargetAddr<'static>, @@ -268,6 +276,7 @@ impl RendezvousMediator { pub fn restart() { SHOULD_EXIT.store(true, Ordering::SeqCst); MANUAL_RESTARTED.store(true, Ordering::SeqCst); + MANUAL_RESTART_NOTIFY.notify_waiters(); log::info!("server restart"); } @@ -339,10 +348,12 @@ impl RendezvousMediator { } Config::reset_online(); let timeout = *timeout.read().unwrap(); + // Subscribe before checking the flag so a restart cannot miss the wait. + let restarted = MANUAL_RESTART_NOTIFY.notified(); if !MANUAL_RESTARTED.load(Ordering::SeqCst) { let elapsed = conn_start_time.elapsed().as_millis() as u64; if elapsed < timeout { - sleep(((timeout - elapsed) / 1000) as _).await; + wait_for_retry(((timeout - elapsed) / 1000) as _, restarted).await; } } else { // https://github.com/rustdesk/rustdesk/issues/12233 @@ -1719,11 +1730,33 @@ impl Drop for CheckIfResendPk { #[cfg(test)] mod tests { use super::{ - connection_meta, mpsc, tokio, AnswererSlot, IceRoute, IntoTargetAddr, PunchHoleSent, - RendezvousMediator, RendezvousMessage, MAX_WEBRTC_ANSWERERS, TCP_PUNCHES, UDP_PUNCHES, + connection_meta, mpsc, tokio, wait_for_retry, AnswererSlot, Duration, IceRoute, + IntoTargetAddr, Ordering, PunchHoleSent, RendezvousMediator, RendezvousMessage, + CONNECT_TIMEOUT, MANUAL_RESTARTED, MANUAL_RESTART_NOTIFY, MAX_WEBRTC_ANSWERERS, + SHOULD_EXIT, TCP_PUNCHES, UDP_PUNCHES, }; use hbb_common::protobuf::Message as _; + #[tokio::test] + async fn manual_restart_interrupts_retry_wait() { + const WAKE_TIMEOUT: Duration = Duration::from_secs(1); + let waiting = wait_for_retry( + (CONNECT_TIMEOUT / 1000) as _, + MANUAL_RESTART_NOTIFY.notified(), + ); + tokio::pin!(waiting); + assert!(hbb_common::futures::poll!(waiting.as_mut()).is_pending()); + + RendezvousMediator::restart(); + let result = tokio::time::timeout(WAKE_TIMEOUT, waiting).await; + MANUAL_RESTARTED.store(false, Ordering::SeqCst); + SHOULD_EXIT.store(false, Ordering::SeqCst); + assert!( + result.is_ok(), + "restart must interrupt the pending retry delay" + ); + } + // The places are the bound and they come back on drop: a punch past the limit is declined // while the ones in flight are left alone, and the next one in is admitted only once a // place has actually been given back. From 94c321d05ce02ee73304e6cf99187e2d4d96e409 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 31 Oct 2026 23:13:40 +0800 Subject: [PATCH 5/9] fix(ci): fail Windows signing on error and retry transient HTTP failures res/job.py sign_files exited 0 when a file was left unsigned: a sign task that never reached "done" only broke out of the current directory, and a failed download of the signed file was ignored, keeping the unsigned original in place. Both now exit non-zero, so the Windows signing steps fail the job. Retry requests.get/post on connection-level errors with the same wrapper the signing service's client uses, rewinding upload streams before each retry. Tunable via HTTP_RETRY_MAX_TRIES and HTTP_RETRY_SLEEP_SECS. Co-Authored-By: Claude Opus 5.5 --- res/job.py | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/res/job.py b/res/job.py index e53105fd3..22703f86e 100755 --- a/res/job.py +++ b/res/job.py @@ -6,8 +6,48 @@ import time import argparse import logging import shutil +import sys import zipfile + +def _file_stream_positions(files): + if not files: + return [] + values = files.values() if hasattr(files, "values") else (item[1] for item in files) + positions = [] + for value in values: + stream = value[1] if isinstance(value, tuple) and len(value) > 1 else value + if hasattr(stream, "tell") and hasattr(stream, "seek"): + try: + positions.append((stream, stream.tell())) + except (OSError, ValueError): + pass + return positions + + +def _with_simple_retries(fn, max_attempts=8, sleep_secs=1.0): + def _wrapper(*args, **kwargs): + file_positions = _file_stream_positions(kwargs.get("files")) + for i in range(max_attempts): + try: + if i: + for stream, position in file_positions: + stream.seek(position) + return fn(*args, **kwargs) + except Exception as e: + logging.warning(f"{fn.__name__} attempt {i+1}/{max_attempts} failed: {e}") + if i == max_attempts - 1: + raise + time.sleep(sleep_secs) + return _wrapper + + +HTTP_RETRY_MAX_TRIES = int(os.getenv("HTTP_RETRY_MAX_TRIES") or "8") +HTTP_RETRY_SLEEP_SECS = float(os.getenv("HTTP_RETRY_SLEEP_SECS") or "1") + +requests.get = _with_simple_retries(requests.get, max_attempts=HTTP_RETRY_MAX_TRIES, sleep_secs=HTTP_RETRY_SLEEP_SECS) +requests.post = _with_simple_retries(requests.post, max_attempts=HTTP_RETRY_MAX_TRIES, sleep_secs=HTTP_RETRY_SLEEP_SECS) + logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s [%(filename)s:%(lineno)d]", @@ -164,10 +204,13 @@ def sign_one_file(file_path): n += 1 status = get_status(task_id) if status and status.get("state") == "done": - download_one_file( + downloaded = download_one_file( task_id, os.path.basename(file_path), os.path.dirname(file_path) ) delete_task(task_id) + if not downloaded: + logging.error(f"Failed to download signed {file_path}") + return False logging.info(f"Signed {file_path}") return True return False @@ -217,7 +260,7 @@ def sign_files(dir_path, only_ext=None): if ext in SIGN_EXTENSIONS: if not sign_one_file(file_path): logging.error(f"Failed to sign {file_path}") - break + sys.exit(1) def main(): From c5201d600bb2c05a941ce9228cec030b2c60d7a2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 1 Nov 2026 13:39:15 +0800 Subject: [PATCH 6/9] Allow trusted devices for direct IP connections (#16778) * Allow trusted devices for direct IP connections Signed-off-by: fufesou * test: preserve existing V2 trusted-device credentials Signed-off-by: fufesou --------- Signed-off-by: fufesou --- src/server/connection/trusted_devices.rs | 2 +- src/trusted_devices/client.rs | 26 ++++++++++++++++++++---- src/trusted_devices/tests.rs | 1 + src/trusted_devices/tests/credentials.rs | 23 +++++++++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/trusted_devices/tests/credentials.rs diff --git a/src/server/connection/trusted_devices.rs b/src/server/connection/trusted_devices.rs index d058a20ff..2f70374d3 100644 --- a/src/server/connection/trusted_devices.rs +++ b/src/server/connection/trusted_devices.rs @@ -9,7 +9,7 @@ use base::message_proto::LoginResponse; impl Connection { pub(super) async fn begin_trusted_device(&mut self) -> bool { if self.conn_audit_primary_auth != ConnAuditPrimaryAuth::PermanentPassword - || !self.stream.is_secured() + || (!self.stream.is_secured() && !crate::common::is_direct_ip_access(&self.lr.username)) || self.require_2fa.is_none() || self.from_switch || !hbb_common::config::Config::get_bool_option( diff --git a/src/trusted_devices/client.rs b/src/trusted_devices/client.rs index 3a52e175a..96a3ae92c 100644 --- a/src/trusted_devices/client.rs +++ b/src/trusted_devices/client.rs @@ -52,6 +52,12 @@ impl Session { secured: bool, key: Option<&[u8]>, ) -> ResultType { + if crate::common::is_direct_ip_access(login.get_id()) { + return Ok(Self { + credentials: Some(Credentials::direct_ip(login.get_id())), + ..Default::default() + }); + } let Some(key) = key.filter(|_| secured) else { return Ok(Self::default()); }; @@ -137,7 +143,7 @@ impl Session { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] struct Issuer { namespace: String, - public_key: [u8; TOKEN_BYTES], + public_key: Option<[u8; TOKEN_BYTES]>, } #[derive(Serialize, Deserialize)] @@ -153,14 +159,26 @@ pub(super) struct Credentials { } impl Credentials { + fn direct_ip(peer_id: &str) -> Self { + Self { + peer_id: peer_id.to_owned(), + issuer: Issuer { + namespace: format!("direct-ip:{peer_id}"), + public_key: None, + }, + } + } + pub fn new(peer_id: String, namespace: String, public_key: &[u8]) -> ResultType { Ok(Self { peer_id, issuer: Issuer { namespace, - public_key: public_key - .try_into() - .map_err(|_| anyhow!("Invalid trusted host public key"))?, + public_key: Some( + public_key + .try_into() + .map_err(|_| anyhow!("Invalid trusted host public key"))?, + ), }, }) } diff --git a/src/trusted_devices/tests.rs b/src/trusted_devices/tests.rs index 6f1715c8f..109b3adbe 100644 --- a/src/trusted_devices/tests.rs +++ b/src/trusted_devices/tests.rs @@ -11,6 +11,7 @@ use hbb_common::{ use std::fs; mod configuration; +mod credentials; mod recovery; const PASSWORD: &str = "permanent-test-password"; diff --git a/src/trusted_devices/tests/credentials.rs b/src/trusted_devices/tests/credentials.rs new file mode 100644 index 000000000..312fc990a --- /dev/null +++ b/src/trusted_devices/tests/credentials.rs @@ -0,0 +1,23 @@ +use super::*; +use hbb_common::password_security::symmetric_crypt; + +#[test] +fn id_credentials_saved_before_direct_ip_support_remain_readable() { + const NAMESPACE: &str = "[\"rs.example.com\"]"; + const PUBLIC_KEY: [u8; TOKEN_BYTES] = [1; TOKEN_BYTES]; + + initialize_config(); + let peer_id = uuid::Uuid::new_v4().to_string(); + let token = random_token().unwrap(); + let saved = serde_json::json!({ + "issuer": { + "namespace": NAMESPACE, + "public_key": PUBLIC_KEY, + }, + "ciphertext": symmetric_crypt(&token, true).unwrap(), + }); + PeerConfig::set_trusted_device_v2(&peer_id, saved.to_string(), Default::default()).unwrap(); + + let credentials = Credentials::new(peer_id, NAMESPACE.into(), &PUBLIC_KEY).unwrap(); + assert_eq!(credentials.load().unwrap(), Some(token.to_vec())); +} From fa611556f84a36d1d1484d335dae2d1ec251b787 Mon Sep 17 00:00:00 2001 From: Maison da Silva Date: Sun, 1 Nov 2026 02:39:33 -0300 Subject: [PATCH 7/9] Clarify auto login requirements in Portuguese (#16792) * Clarify auto login requirements in Portuguese Updated the auto login message to clarify requirements. * Fix translation for terminal admin login tip Updated the translation for the terminal admin login tip in Portuguese. --- src/lang/pt_BR.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/pt_BR.rs b/src/lang/pt_BR.rs index 7008ac620..21c636a5b 100644 --- a/src/lang/pt_BR.rs +++ b/src/lang/pt_BR.rs @@ -187,7 +187,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter your password", "Informe sua senha"), ("Logging in...", "Fazendo Login..."), ("Enable RDP session sharing", "Habilitar compartilhamento de sessão RDP"), - ("Auto Login", "Login Automático (Somente válido se você habilitou \"Bloquear após o fim da sessão\")"), + ("Auto Login", "Login Automático (Para que o recurso funcione corretamente, é necessário habilitar \"Bloquear após o fim da sessão\" nas configurações do sistema.)"), ("Enable direct IP access", "Habilitar Acesso IP Direto"), ("Rename", "Renomear"), ("Space", "Espaço"), @@ -686,7 +686,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("New tab", "Nova aba"), ("Keep terminal sessions on disconnect", "Manter sessões de terminal ao desconectar"), ("Terminal (Run as administrator)", "Terminal (Executar como administrador)"), - ("terminal-admin-login-tip", "Insira o nome do usuário e senha de administrador do dispositivo controlado."), + ("terminal-admin-login-tip", "Insira o nome do usuário e senha de administrador do dispositivo."), ("Failed to get user token.", "Falha ao obter token do usuário."), ("Incorrect username or password.", "Usuário ou senha incorretos"), ("The user is not an administrator.", "O usuário não é administrador"), From 82301f1776ef8ab68229c8ad5199d6aedcc3b96b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 1 Nov 2026 13:39:54 +0800 Subject: [PATCH 8/9] Fix/windows install directory confirmation (#16783) * fix(windows): warn before removing installation directory Signed-off-by: fufesou * fix(windows): show inline installation directory warning Signed-off-by: fufesou * refactor(windows): simplify installation directory warning Key the warning widget by its path and remove the optional widget test suite. Signed-off-by: fufesou * fix(windows): limit installation warnings to the selected directory Share install-directory-not-empty-tip between inline and confirmation warnings, and remove the separate previous-installation warning. Signed-off-by: fufesou * fix(windows): skip directory warnings when reinstalling in place Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/desktop/pages/install_page.dart | 72 ++++++++++---- .../install_directory_confirmation.dart | 96 +++++++++++++++++++ src/lang/ar.rs | 1 + src/lang/az.rs | 1 + src/lang/be.rs | 1 + src/lang/bg.rs | 1 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/en.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/et.rs | 1 + src/lang/eu.rs | 1 + src/lang/fa.rs | 1 + src/lang/fi.rs | 1 + src/lang/fr.rs | 1 + src/lang/ge.rs | 1 + src/lang/gl.rs | 1 + src/lang/gu.rs | 1 + src/lang/he.rs | 1 + src/lang/hi.rs | 1 + src/lang/hr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/lv.rs | 1 + src/lang/ml.rs | 1 + src/lang/nb.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_BR.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sc.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/ta.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/uk.rs | 1 + src/lang/ur.rs | 1 + src/lang/vi.rs | 1 + 56 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 flutter/lib/desktop/widgets/install_directory_confirmation.dart diff --git a/flutter/lib/desktop/pages/install_page.dart b/flutter/lib/desktop/pages/install_page.dart index 8e72f8d31..58ff028f2 100644 --- a/flutter/lib/desktop/pages/install_page.dart +++ b/flutter/lib/desktop/pages/install_page.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/desktop/widgets/install_directory_confirmation.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; @@ -164,27 +165,46 @@ class _InstallPageBodyState extends State<_InstallPageBody> children: [ Text(translate('Installation'), style: Theme.of(context).textTheme.headlineMedium), - Row( + Table( + columnWidths: const { + 0: IntrinsicColumnWidth(), + 2: IntrinsicColumnWidth(), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ - Text('${translate('Installation Path')}:') - .marginOnly(right: 10), - Expanded( - child: TextField( - controller: controller, - readOnly: true, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(0.75 * em), + TableRow( + children: [ + Text('${translate('Installation Path')}:') + .marginOnly(right: 10), + TextField( + controller: controller, + readOnly: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(0.75 * em), + ), + ).workaroundFreezeLinuxMint().marginOnly(right: 10), + Obx( + () => OutlinedButton.icon( + icon: Icon(Icons.folder_outlined, size: 16), + onPressed: + btnEnabled.value ? selectInstallPath : null, + style: buttonStyle, + label: Text(translate('Change Path')), + ), ), - ).workaroundFreezeLinuxMint().marginOnly(right: 10), + ], + ), + TableRow( + children: [ + const SizedBox.shrink(), + ValueListenableBuilder( + valueListenable: controller, + builder: (context, value, child) => + InstallDirectoryWarning(directoryPath: value.text), + ).marginOnly(right: 10), + const SizedBox.shrink(), + ], ), - Obx( - () => OutlinedButton.icon( - icon: Icon(Icons.folder_outlined, size: 16), - onPressed: btnEnabled.value ? selectInstallPath : null, - style: buttonStyle, - label: Text(translate('Change Path')), - ), - ) ], ).marginSymmetric(vertical: 2 * em), Option(startmenu, label: 'Create start menu shortcuts') @@ -277,7 +297,9 @@ class _InstallPageBodyState extends State<_InstallPageBody> )); } - void install() { + Future install() async { + if (!btnEnabled.value) return; + final installPath = controller.text; do_install() { btnEnabled.value = false; cancelEnabled.value = false; @@ -286,10 +308,18 @@ class _InstallPageBodyState extends State<_InstallPageBody> if (startmenu.value) args += ' startmenu'; if (desktopicon.value) args += ' desktopicon'; if (printer.value) args += ' printer'; - bind.installInstallMe(options: args, path: controller.text); + bind.installInstallMe(options: args, path: installPath); } - do_install(); + btnEnabled.value = false; + try { + final confirmed = + await confirmInstallDirectory(this.context, targetPath: installPath); + if (!mounted || !confirmed) return; + do_install(); + } finally { + if (mounted && !showProgress.value) btnEnabled.value = true; + } } void selectInstallPath() async { diff --git a/flutter/lib/desktop/widgets/install_directory_confirmation.dart b/flutter/lib/desktop/widgets/install_directory_confirmation.dart new file mode 100644 index 000000000..d48b203a9 --- /dev/null +++ b/flutter/lib/desktop/widgets/install_directory_confirmation.dart @@ -0,0 +1,96 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; +import 'package:path/path.dart' as path; + +const _nonemptyDirectoryWarning = 'install-directory-not-empty-tip'; + +Future confirmInstallDirectory(BuildContext context, + {required String targetPath}) async { + try { + final needsWarning = await _needsDirectoryWarning(targetPath); + if (!context.mounted) return false; + if (!needsWarning) return true; + return await _showDirectoryDialog( + context, '${translate(_nonemptyDirectoryWarning)}\n$targetPath'); + } on FileSystemException catch (error) { + debugPrint('Failed to inspect installation directory: $error'); + if (context.mounted) { + await _showDirectoryDialog(context, error.toString(), isError: true); + } + return false; + } +} + +Future _needsDirectoryWarning(String directoryPath) async { + if (bind.mainIsInstalled() && + path.equals(directoryPath, bind.installInstallPath())) { + return false; + } + final directory = Directory(directoryPath); + return await directory.exists() && + !await directory.list(followLinks: false).isEmpty; +} + +Future _showDirectoryDialog(BuildContext context, String message, + {bool isError = false}) async { + final confirmed = await showDialog( + context: context, + builder: (dialogContext) => AlertDialog( + scrollable: true, + title: Text(translate(isError ? 'Error' : 'Warning')), + content: SelectableText(message), + actions: [ + dialogButton(isError ? 'OK' : 'Cancel', + isOutline: !isError, + onPressed: () => Navigator.of(dialogContext).pop(false)), + if (!isError) + dialogButton('Continue', + onPressed: () => Navigator.of(dialogContext).pop(true)), + ], + ), + ); + return confirmed == true; +} + +class InstallDirectoryWarning extends StatefulWidget { + InstallDirectoryWarning({required this.directoryPath}) + : super(key: ValueKey(directoryPath)); + + final String directoryPath; + + @override + State createState() => + _InstallDirectoryWarningState(); +} + +class _InstallDirectoryWarningState extends State { + static const _spacing = 8.0; + late final Future _needsWarning = + _needsDirectoryWarning(widget.directoryPath); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _needsWarning, + builder: (context, snapshot) { + if (!snapshot.hasError && snapshot.data != true) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.symmetric(vertical: _spacing), + child: Text( + snapshot.hasError + ? '${translate('Error')}: ${snapshot.error}' + : translate(_nonemptyDirectoryWarning), + style: TextStyle( + color: snapshot.hasError + ? Theme.of(context).colorScheme.error + : Colors.orange.shade700)), + ); + }, + ); + } +} diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 1ebc7c757..873b5fed5 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "الرجاء اختيار Android → media."), ("Trust duration (days)", "مدة الثقة (بالأيام)"), ("Renew trust", "تجديد الثقة"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/az.rs b/src/lang/az.rs index 714f4110f..eff045444 100644 --- a/src/lang/az.rs +++ b/src/lang/az.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Zəhmət olmasa, Android → media seçin."), ("Trust duration (days)", "Etibar müddəti (gün)"), ("Renew trust", "Etibarı yenilə"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index cadc4e797..f3b5244ef 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Выберыце Android → media."), ("Trust duration (days)", "Тэрмін даверу (у днях)"), ("Renew trust", "Падоўжыць давер"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index c8ed6cf42..4b9be09da 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Моля, изберете Android → media."), ("Trust duration (days)", "Срок на доверие (дни)"), ("Renew trust", "Подновяване на доверието"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index d8b7dfc14..08a9610c9 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seleccioneu Android → media."), ("Trust duration (days)", "Durada de la confiança (dies)"), ("Renew trust", "Renova la confiança"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 98e97631f..9f48c718e 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "请选择 Android → media。"), ("Trust duration (days)", "信任有效期(天)"), ("Renew trust", "延长信任有效期"), + ("install-directory-not-empty-tip", "所选文件夹不为空。安装可能删除现有数据。建议选择其他文件夹。"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 721051999..03f2a02ed 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vyberte prosím Android → media."), ("Trust duration (days)", "Doba důvěry (dny)"), ("Renew trust", "Obnovit důvěru"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index e3ed60033..79c1f8bbe 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vælg Android → media."), ("Trust duration (days)", "Tillidsperiode (dage)"), ("Renew trust", "Forny tillid"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index a03e3a260..88337466d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Bitte wählen Sie Android → media."), ("Trust duration (days)", "Vertrauensdauer (Tage)"), ("Renew trust", "Vertrauen erneuern"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 1a5799e83..ab0ac36cf 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Παρακαλώ επιλέξτε Android → media."), ("Trust duration (days)", "Διάρκεια εμπιστοσύνης (ημέρες)"), ("Renew trust", "Ανανέωση εμπιστοσύνης"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index f3c654f0f..72b666174 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -291,5 +291,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("api_server_connection_tip", "Your custom server uses unencrypted HTTP. Contact your administrator to enable HTTPS."), ("api_server_empty_tip", "With API Server empty, your custom server defaults to HTTP. Contact your administrator to enable HTTPS."), ("android-logs-folder-tip", "Choose Android → media to store log files."), + ("install-directory-not-empty-tip", "The selected folder is not empty. Installation may delete existing data. Consider choosing a different folder."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index fccb8616f..88a4a2a70 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Bonvolu elekti Android → media."), ("Trust duration (days)", "Daŭro de fido (tagoj)"), ("Renew trust", "Renovigi fidon"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index ff9127d1b..9adb6d7cd 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seleccione Android → media."), ("Trust duration (days)", "Duración de la confianza (días)"), ("Renew trust", "Renovar confianza"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index a7d8980ef..1e33b966e 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Palun vali Android → media."), ("Trust duration (days)", "Usalduse kestus (päevades)"), ("Renew trust", "Uuenda usaldust"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index dc00c810a..9f6ebb72c 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Hautatu Android → media."), ("Trust duration (days)", "Konfiantzaren iraupena (egunak)"), ("Renew trust", "Berritu konfiantza"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 66d5dc79c..ec7ae95a0 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "لطفاً Android → media را انتخاب کنید."), ("Trust duration (days)", "مدت اعتماد (روز)"), ("Renew trust", "تمدید اعتماد"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fi.rs b/src/lang/fi.rs index d4277de0f..c50c24729 100644 --- a/src/lang/fi.rs +++ b/src/lang/fi.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Valitse Android → media."), ("Trust duration (days)", "Luottamuksen kesto (päivää)"), ("Renew trust", "Jatka luottamusta"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 72376b909..d68932b42 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Veuillez sélectionner Android → media."), ("Trust duration (days)", "Durée de confiance (jours)"), ("Renew trust", "Renouveler la confiance"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ge.rs b/src/lang/ge.rs index fe76e3551..bdf7cd0e3 100644 --- a/src/lang/ge.rs +++ b/src/lang/ge.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "გთხოვთ, აირჩიოთ Android → media."), ("Trust duration (days)", "ნდობის ხანგრძლივობა (დღე)"), ("Renew trust", "ნდობის განახლება"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gl.rs b/src/lang/gl.rs index 1cade2857..b6d6270fe 100644 --- a/src/lang/gl.rs +++ b/src/lang/gl.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selecciona Android → media."), ("Trust duration (days)", "Duración da confianza (días)"), ("Renew trust", "Renovar a confianza"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gu.rs b/src/lang/gu.rs index c73cdb5b5..51c952fe0 100644 --- a/src/lang/gu.rs +++ b/src/lang/gu.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "કૃપા કરીને Android → media પસંદ કરો."), ("Trust duration (days)", "વિશ્વાસ અવધિ (દિવસો)"), ("Renew trust", "વિશ્વાસ નવીકરણ કરો"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 102a129bc..cc4c23a09 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "אנא בחר Android → media."), ("Trust duration (days)", "משך האמון (ימים)"), ("Renew trust", "חידוש אמון"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hi.rs b/src/lang/hi.rs index ce5dc2140..016b77405 100644 --- a/src/lang/hi.rs +++ b/src/lang/hi.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "कृपया Android → media चुनें।"), ("Trust duration (days)", "विश्वास अवधि (दिन)"), ("Renew trust", "विश्वास नवीनीकृत करें"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index bef672007..9c3ca651d 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Odaberite Android → media."), ("Trust duration (days)", "Trajanje povjerenja (dani)"), ("Renew trust", "Obnovi povjerenje"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 145914e3c..d83fdc0e3 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Válassza az Android → media mappát."), ("Trust duration (days)", "Megbízhatóság időtartama (nap)"), ("Renew trust", "Megbízhatóság megújítása"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 364d542fc..b45dd0d6b 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Silakan pilih Android → media."), ("Trust duration (days)", "Durasi kepercayaan (hari)"), ("Renew trust", "Perbarui kepercayaan"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 42d74e8a8..8b9bdc2d2 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seleziona Android → media."), ("Trust duration (days)", "Durata attendibilità (giorni)"), ("Renew trust", "Rinnova attendibilità"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 3239326c0..1d8543b49 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media を選択してください。"), ("Trust duration (days)", "信頼期間 (日)"), ("Renew trust", "信頼を更新"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 490e780e1..c4414949e 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media를 선택해 주시기 바랍니다."), ("Trust duration (days)", "신뢰 유지 기간 (일)"), ("Renew trust", "신뢰 갱신"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 9dbdf611f..c5d9a340a 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media таңдаңыз."), ("Trust duration (days)", "Сенім мерзімі (күн)"), ("Renew trust", "Сенімді жаңарту"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index bd4f1ce6c..f9351ef34 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Pasirinkite Android → media."), ("Trust duration (days)", "Pasitikėjimo trukmė (dienomis)"), ("Renew trust", "Atnaujinti pasitikėjimą"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index f00b2fe4a..93e117182 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Lūdzu, atlasiet Android → media."), ("Trust duration (days)", "Uzticēšanās ilgums (dienas)"), ("Renew trust", "Atjaunot uzticēšanos"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ml.rs b/src/lang/ml.rs index e5be24a38..dcdd60503 100644 --- a/src/lang/ml.rs +++ b/src/lang/ml.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "ദയവായി Android → media തിരഞ്ഞെടുക്കുക."), ("Trust duration (days)", "വിശ്വാസ കാലാവധി (ദിവസം)"), ("Renew trust", "വിശ്വാസം പുതുക്കുക"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 33ef45e7e..3a20c57ab 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Velg Android → media."), ("Trust duration (days)", "Klareringsperiode (dager)"), ("Renew trust", "Forny klarering"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index e5dad3719..2c9a5484c 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selecteer Android → media."), ("Trust duration (days)", "Vertrouwensperiode (dagen)"), ("Renew trust", "Vertrouwen hernieuwen"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 192c36604..d70da06de 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Wybierz Android → media."), ("Trust duration (days)", "Okres zaufania (dni)"), ("Renew trust", "Odnów zaufanie"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_BR.rs b/src/lang/pt_BR.rs index 21c636a5b..7fbae4ce8 100644 --- a/src/lang/pt_BR.rs +++ b/src/lang/pt_BR.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selecione Android → media."), ("Trust duration (days)", "Duração da confiança (dias)"), ("Renew trust", "Renovar confiança"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 1adcb6e7e..1a60f8ae7 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Por favor selecione Android → media."), ("Trust duration (days)", "Duração da confiança (dias)"), ("Renew trust", "Renovar confiança"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 05cce6ab8..56918f750 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selectează Android → media."), ("Trust duration (days)", "Durata încrederii (zile)"), ("Renew trust", "Reînnoiește încrederea"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index dc7757219..3b5fed87d 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Выберите Android → media."), ("Trust duration (days)", "Срок доверия (в днях)"), ("Renew trust", "Продлить доверие"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sc.rs b/src/lang/sc.rs index f17608d4d..166f9c262 100644 --- a/src/lang/sc.rs +++ b/src/lang/sc.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seletziona Android → media."), ("Trust duration (days)", "Durada de sa fidùtzia (dies)"), ("Renew trust", "Annoa sa fidùtzia"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 7d5faf167..75113960d 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vyberte Android → media."), ("Trust duration (days)", "Doba dôvery (dni)"), ("Renew trust", "Obnoviť dôveru"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 1107246c7..a15c02f8e 100644 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Izberite Android → media."), ("Trust duration (days)", "Trajanje zaupanja (dni)"), ("Renew trust", "Obnovi zaupanje"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 3d329af4c..69669749b 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Ju lutemi zgjidhni Android → media."), ("Trust duration (days)", "Kohëzgjatja e besimit (ditë)"), ("Renew trust", "Rinovo besimin"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index def1efeb8..6e1d73783 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Izaberite Android → media."), ("Trust duration (days)", "Trajanje poverenja (dani)"), ("Renew trust", "Obnovi poverenje"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 5aad93760..49c7e328b 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Välj Android → media."), ("Trust duration (days)", "Förtroendeperiod (dagar)"), ("Renew trust", "Förnya förtroende"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ta.rs b/src/lang/ta.rs index 51ba9f8bc..de48cc36e 100644 --- a/src/lang/ta.rs +++ b/src/lang/ta.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media என்பதைத் தேர்ந்தெடுக்கவும்."), ("Trust duration (days)", "நம்பிக்கைக் காலம் (நாட்கள்)"), ("Renew trust", "நம்பிக்கையைப் புதுப்பி"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 0ca3c660b..567d694a8 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", ""), ("Trust duration (days)", ""), ("Renew trust", ""), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 14deca923..736f33bd5 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "กรุณาเลือก Android → media"), ("Trust duration (days)", "ระยะเวลาการเชื่อถือ (วัน)"), ("Renew trust", "ต่ออายุการเชื่อถือ"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 44dd56e7e..b90e4ad0a 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Lütfen Android → media konumunu seçin."), ("Trust duration (days)", "Güven süresi (gün)"), ("Renew trust", "Güveni yenile"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 793e8d0b1..a0f923dd4 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "請選擇 Android → media。"), ("Trust duration (days)", "信任有效期(天)"), ("Renew trust", "延長信任期限"), + ("install-directory-not-empty-tip", "所選資料夾不是空的。安裝可能刪除現有資料。建議選擇其他資料夾。"), ].iter().cloned().collect(); } diff --git a/src/lang/uk.rs b/src/lang/uk.rs index 5a3a3f7fc..b86cfb7bd 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Будь ласка, виберіть Android → media."), ("Trust duration (days)", "Термін довіри (у днях)"), ("Renew trust", "Подовжити довіру"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ur.rs b/src/lang/ur.rs index ad2f10513..0cfaf2543 100644 --- a/src/lang/ur.rs +++ b/src/lang/ur.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "براہ کرم Android → media منتخب کریں۔"), ("Trust duration (days)", "اعتماد کی مدت (دن)"), ("Renew trust", "اعتماد کی تجدید کریں"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vi.rs b/src/lang/vi.rs index a51fd0473..af8e4464e 100644 --- a/src/lang/vi.rs +++ b/src/lang/vi.rs @@ -823,5 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vui lòng chọn Android → media."), ("Trust duration (days)", "Thời hạn tin cậy (ngày)"), ("Renew trust", "Gia hạn tin cậy"), + ("install-directory-not-empty-tip", ""), ].iter().cloned().collect(); } From 23e5bb471a040d62f0a6ad52591fb30784265b73 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 1 Nov 2026 13:50:20 +0800 Subject: [PATCH 9/9] finish translations Fill install-directory-not-empty-tip (added in #16783) for every language, including it.rs at the maintainer's request. Co-Authored-By: Claude Opus 5.5 --- src/lang/ar.rs | 2 +- src/lang/az.rs | 2 +- src/lang/be.rs | 2 +- src/lang/bg.rs | 2 +- src/lang/ca.rs | 2 +- src/lang/cs.rs | 2 +- src/lang/da.rs | 2 +- src/lang/de.rs | 2 +- src/lang/el.rs | 2 +- src/lang/eo.rs | 2 +- src/lang/es.rs | 2 +- src/lang/et.rs | 2 +- src/lang/eu.rs | 2 +- src/lang/fa.rs | 2 +- src/lang/fi.rs | 2 +- src/lang/fr.rs | 2 +- src/lang/ge.rs | 2 +- src/lang/gl.rs | 2 +- src/lang/gu.rs | 2 +- src/lang/he.rs | 2 +- src/lang/hi.rs | 2 +- src/lang/hr.rs | 2 +- src/lang/hu.rs | 2 +- src/lang/id.rs | 2 +- src/lang/it.rs | 2 +- src/lang/ja.rs | 2 +- src/lang/ko.rs | 2 +- src/lang/kz.rs | 2 +- src/lang/lt.rs | 2 +- src/lang/lv.rs | 2 +- src/lang/ml.rs | 2 +- src/lang/nb.rs | 2 +- src/lang/nl.rs | 2 +- src/lang/pl.rs | 2 +- src/lang/pt_BR.rs | 2 +- src/lang/pt_PT.rs | 2 +- src/lang/ro.rs | 2 +- src/lang/ru.rs | 2 +- src/lang/sc.rs | 2 +- src/lang/sk.rs | 2 +- src/lang/sl.rs | 2 +- src/lang/sq.rs | 2 +- src/lang/sr.rs | 2 +- src/lang/sv.rs | 2 +- src/lang/ta.rs | 2 +- src/lang/th.rs | 2 +- src/lang/tr.rs | 2 +- src/lang/uk.rs | 2 +- src/lang/ur.rs | 2 +- src/lang/vi.rs | 2 +- 50 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 873b5fed5..d37250df8 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "الرجاء اختيار Android → media."), ("Trust duration (days)", "مدة الثقة (بالأيام)"), ("Renew trust", "تجديد الثقة"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "المجلد المحدد ليس فارغًا. قد يؤدي التثبيت إلى حذف البيانات الموجودة. يُنصح باختيار مجلد آخر."), ].iter().cloned().collect(); } diff --git a/src/lang/az.rs b/src/lang/az.rs index eff045444..40018e5a2 100644 --- a/src/lang/az.rs +++ b/src/lang/az.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Zəhmət olmasa, Android → media seçin."), ("Trust duration (days)", "Etibar müddəti (gün)"), ("Renew trust", "Etibarı yenilə"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Seçilmiş qovluq boş deyil. Quraşdırma mövcud məlumatları silə bilər. Başqa qovluq seçməyiniz tövsiyə olunur."), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index f3b5244ef..77cad7c70 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Выберыце Android → media."), ("Trust duration (days)", "Тэрмін даверу (у днях)"), ("Renew trust", "Падоўжыць давер"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Выбраная папка не пустая. Усталёўванне можа выдаліць наяўныя даныя. Лепш выберыце іншую папку."), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 4b9be09da..efe791cbd 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Моля, изберете Android → media."), ("Trust duration (days)", "Срок на доверие (дни)"), ("Renew trust", "Подновяване на доверието"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Избраната папка не е празна. Инсталацията може да изтрие съществуващите данни. Препоръчително е да изберете друга папка."), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 08a9610c9..f7d186d96 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seleccioneu Android → media."), ("Trust duration (days)", "Durada de la confiança (dies)"), ("Renew trust", "Renova la confiança"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "La carpeta seleccionada no és buida. La instal·lació pot suprimir les dades existents. Considereu triar una altra carpeta."), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 03f2a02ed..37871d8c1 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vyberte prosím Android → media."), ("Trust duration (days)", "Doba důvěry (dny)"), ("Renew trust", "Obnovit důvěru"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Vybraná složka není prázdná. Instalace může odstranit existující data. Zvažte výběr jiné složky."), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 79c1f8bbe..f17b21d02 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vælg Android → media."), ("Trust duration (days)", "Tillidsperiode (dage)"), ("Renew trust", "Forny tillid"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Den valgte mappe er ikke tom. Installationen kan slette eksisterende data. Overvej at vælge en anden mappe."), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 88337466d..e71e52b0b 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Bitte wählen Sie Android → media."), ("Trust duration (days)", "Vertrauensdauer (Tage)"), ("Renew trust", "Vertrauen erneuern"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Der ausgewählte Ordner ist nicht leer. Bei der Installation können vorhandene Daten gelöscht werden. Erwägen Sie, einen anderen Ordner auszuwählen."), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index ab0ac36cf..ca0572d76 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Παρακαλώ επιλέξτε Android → media."), ("Trust duration (days)", "Διάρκεια εμπιστοσύνης (ημέρες)"), ("Renew trust", "Ανανέωση εμπιστοσύνης"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Ο επιλεγμένος φάκελος δεν είναι κενός. Η εγκατάσταση ενδέχεται να διαγράψει υπάρχοντα δεδομένα. Εξετάστε το ενδεχόμενο να επιλέξετε διαφορετικό φάκελο."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 88a4a2a70..980667233 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Bonvolu elekti Android → media."), ("Trust duration (days)", "Daŭro de fido (tagoj)"), ("Renew trust", "Renovigi fidon"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "La elektita dosierujo ne estas malplena. La instalado povas forigi ekzistantajn datumojn. Konsideru elekti alian dosierujon."), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 9adb6d7cd..7d0742237 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seleccione Android → media."), ("Trust duration (days)", "Duración de la confianza (días)"), ("Renew trust", "Renovar confianza"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "La carpeta seleccionada no está vacía. La instalación puede eliminar los datos existentes. Considera elegir otra carpeta."), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 1e33b966e..da5e8ecb3 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Palun vali Android → media."), ("Trust duration (days)", "Usalduse kestus (päevades)"), ("Renew trust", "Uuenda usaldust"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Valitud kaust ei ole tühi. Paigaldamine võib olemasolevad andmed kustutada. Kaalu teise kausta valimist."), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index 9f6ebb72c..23a2adc2b 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Hautatu Android → media."), ("Trust duration (days)", "Konfiantzaren iraupena (egunak)"), ("Renew trust", "Berritu konfiantza"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Hautatutako karpeta ez dago hutsik. Instalazioak lehendik dauden datuak ezaba ditzake. Beste karpeta bat aukeratzea gomendatzen da."), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index ec7ae95a0..48b56e944 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "لطفاً Android → media را انتخاب کنید."), ("Trust duration (days)", "مدت اعتماد (روز)"), ("Renew trust", "تمدید اعتماد"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "پوشه انتخاب‌شده خالی نیست. نصب ممکن است داده‌های موجود را حذف کند. بهتر است پوشه دیگری انتخاب کنید."), ].iter().cloned().collect(); } diff --git a/src/lang/fi.rs b/src/lang/fi.rs index c50c24729..fa132eedf 100644 --- a/src/lang/fi.rs +++ b/src/lang/fi.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Valitse Android → media."), ("Trust duration (days)", "Luottamuksen kesto (päivää)"), ("Renew trust", "Jatka luottamusta"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Valittu kansio ei ole tyhjä. Asennus voi poistaa olemassa olevia tietoja. Harkitse toisen kansion valitsemista."), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index d68932b42..3bccf1a48 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Veuillez sélectionner Android → media."), ("Trust duration (days)", "Durée de confiance (jours)"), ("Renew trust", "Renouveler la confiance"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Le dossier sélectionné n’est pas vide. L’installation peut supprimer des données existantes. Envisagez de choisir un autre dossier."), ].iter().cloned().collect(); } diff --git a/src/lang/ge.rs b/src/lang/ge.rs index bdf7cd0e3..6b7c60490 100644 --- a/src/lang/ge.rs +++ b/src/lang/ge.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "გთხოვთ, აირჩიოთ Android → media."), ("Trust duration (days)", "ნდობის ხანგრძლივობა (დღე)"), ("Renew trust", "ნდობის განახლება"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "არჩეული საქაღალდე ცარიელი არ არის. ინსტალაციამ შეიძლება წაშალოს არსებული მონაცემები. რეკომენდებულია სხვა საქაღალდის არჩევა."), ].iter().cloned().collect(); } diff --git a/src/lang/gl.rs b/src/lang/gl.rs index b6d6270fe..da46ff5d8 100644 --- a/src/lang/gl.rs +++ b/src/lang/gl.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selecciona Android → media."), ("Trust duration (days)", "Duración da confianza (días)"), ("Renew trust", "Renovar a confianza"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "O cartafol seleccionado non está baleiro. A instalación pode eliminar os datos existentes. Considera escoller outro cartafol."), ].iter().cloned().collect(); } diff --git a/src/lang/gu.rs b/src/lang/gu.rs index 51c952fe0..5ff2d4f8d 100644 --- a/src/lang/gu.rs +++ b/src/lang/gu.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "કૃપા કરીને Android → media પસંદ કરો."), ("Trust duration (days)", "વિશ્વાસ અવધિ (દિવસો)"), ("Renew trust", "વિશ્વાસ નવીકરણ કરો"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "પસંદ કરેલ ફોલ્ડર ખાલી નથી. ઇન્સ્ટોલેશન હાલનો ડેટા કાઢી નાખી શકે છે. બીજું ફોલ્ડર પસંદ કરવાનું વિચારો."), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index cc4c23a09..666cd22ba 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "אנא בחר Android → media."), ("Trust duration (days)", "משך האמון (ימים)"), ("Renew trust", "חידוש אמון"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "התיקייה שנבחרה אינה ריקה. ההתקנה עלולה למחוק נתונים קיימים. מומלץ לבחור תיקייה אחרת."), ].iter().cloned().collect(); } diff --git a/src/lang/hi.rs b/src/lang/hi.rs index 016b77405..c843867fb 100644 --- a/src/lang/hi.rs +++ b/src/lang/hi.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "कृपया Android → media चुनें।"), ("Trust duration (days)", "विश्वास अवधि (दिन)"), ("Renew trust", "विश्वास नवीनीकृत करें"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "चयनित फ़ोल्डर खाली नहीं है। इंस्टॉलेशन से मौजूदा डेटा हट सकता है। कोई दूसरा फ़ोल्डर चुनने पर विचार करें।"), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 9c3ca651d..38797fecf 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Odaberite Android → media."), ("Trust duration (days)", "Trajanje povjerenja (dani)"), ("Renew trust", "Obnovi povjerenje"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Odabrana mapa nije prazna. Instalacija može izbrisati postojeće podatke. Razmislite o odabiru druge mape."), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index d83fdc0e3..e0ed9784e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Válassza az Android → media mappát."), ("Trust duration (days)", "Megbízhatóság időtartama (nap)"), ("Renew trust", "Megbízhatóság megújítása"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "A kiválasztott mappa nem üres. A telepítés törölheti a meglévő adatokat. Fontolja meg egy másik mappa kiválasztását."), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index b45dd0d6b..a128d3133 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Silakan pilih Android → media."), ("Trust duration (days)", "Durasi kepercayaan (hari)"), ("Renew trust", "Perbarui kepercayaan"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Folder yang dipilih tidak kosong. Instalasi dapat menghapus data yang ada. Pertimbangkan untuk memilih folder lain."), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 8b9bdc2d2..f05500b09 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seleziona Android → media."), ("Trust duration (days)", "Durata attendibilità (giorni)"), ("Renew trust", "Rinnova attendibilità"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "La cartella selezionata non è vuota. L'installazione potrebbe eliminare i dati esistenti. Valuta la possibilità di scegliere una cartella diversa."), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 1d8543b49..1cb2a8c54 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media を選択してください。"), ("Trust duration (days)", "信頼期間 (日)"), ("Renew trust", "信頼を更新"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "選択したフォルダーは空ではありません。インストールにより既存のデータが削除される可能性があります。別のフォルダーを選択することをおすすめします。"), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index c4414949e..3a7fa8676 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media를 선택해 주시기 바랍니다."), ("Trust duration (days)", "신뢰 유지 기간 (일)"), ("Renew trust", "신뢰 갱신"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "선택한 폴더가 비어 있지 않습니다. 설치 시 기존 데이터가 삭제될 수 있습니다. 다른 폴더를 선택하는 것이 좋습니다."), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index c5d9a340a..d46a7940b 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media таңдаңыз."), ("Trust duration (days)", "Сенім мерзімі (күн)"), ("Renew trust", "Сенімді жаңарту"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Таңдалған бума бос емес. Орнату бар деректерді жоюы мүмкін. Басқа буманы таңдаған жөн."), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index f9351ef34..c07d78ec4 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Pasirinkite Android → media."), ("Trust duration (days)", "Pasitikėjimo trukmė (dienomis)"), ("Renew trust", "Atnaujinti pasitikėjimą"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Pasirinktas aplankas nėra tuščias. Diegimas gali ištrinti esamus duomenis. Apsvarstykite galimybę pasirinkti kitą aplanką."), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 93e117182..12076fd90 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Lūdzu, atlasiet Android → media."), ("Trust duration (days)", "Uzticēšanās ilgums (dienas)"), ("Renew trust", "Atjaunot uzticēšanos"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Izvēlētā mape nav tukša. Instalēšana var izdzēst esošos datus. Apsveriet iespēju izvēlēties citu mapi."), ].iter().cloned().collect(); } diff --git a/src/lang/ml.rs b/src/lang/ml.rs index dcdd60503..8944a714d 100644 --- a/src/lang/ml.rs +++ b/src/lang/ml.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "ദയവായി Android → media തിരഞ്ഞെടുക്കുക."), ("Trust duration (days)", "വിശ്വാസ കാലാവധി (ദിവസം)"), ("Renew trust", "വിശ്വാസം പുതുക്കുക"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "തിരഞ്ഞെടുത്ത ഫോൾഡർ ശൂന്യമല്ല. ഇൻസ്റ്റാളേഷൻ നിലവിലുള്ള ഡാറ്റ ഇല്ലാതാക്കിയേക്കാം. മറ്റൊരു ഫോൾഡർ തിരഞ്ഞെടുക്കുന്നത് പരിഗണിക്കുക."), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 3a20c57ab..f1a00cbe3 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Velg Android → media."), ("Trust duration (days)", "Klareringsperiode (dager)"), ("Renew trust", "Forny klarering"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Den valgte mappen er ikke tom. Installasjonen kan slette eksisterende data. Vurder å velge en annen mappe."), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 2c9a5484c..d398e962b 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selecteer Android → media."), ("Trust duration (days)", "Vertrouwensperiode (dagen)"), ("Renew trust", "Vertrouwen hernieuwen"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "De geselecteerde map is niet leeg. De installatie kan bestaande gegevens verwijderen. Overweeg een andere map te kiezen."), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index d70da06de..bf54af504 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Wybierz Android → media."), ("Trust duration (days)", "Okres zaufania (dni)"), ("Renew trust", "Odnów zaufanie"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Wybrany folder nie jest pusty. Instalacja może usunąć istniejące dane. Rozważ wybranie innego folderu."), ].iter().cloned().collect(); } diff --git a/src/lang/pt_BR.rs b/src/lang/pt_BR.rs index 7fbae4ce8..3a9412f28 100644 --- a/src/lang/pt_BR.rs +++ b/src/lang/pt_BR.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selecione Android → media."), ("Trust duration (days)", "Duração da confiança (dias)"), ("Renew trust", "Renovar confiança"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "A pasta selecionada não está vazia. A instalação pode excluir dados existentes. Considere escolher outra pasta."), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 1a60f8ae7..3386d9e4b 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Por favor selecione Android → media."), ("Trust duration (days)", "Duração da confiança (dias)"), ("Renew trust", "Renovar confiança"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "A pasta selecionada não está vazia. A instalação pode eliminar dados existentes. Considere escolher outra pasta."), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 56918f750..cd6fa5bcf 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Selectează Android → media."), ("Trust duration (days)", "Durata încrederii (zile)"), ("Renew trust", "Reînnoiește încrederea"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Folderul selectat nu este gol. Instalarea poate șterge datele existente. Ia în considerare alegerea altui folder."), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 3b5fed87d..771bb6202 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Выберите Android → media."), ("Trust duration (days)", "Срок доверия (в днях)"), ("Renew trust", "Продлить доверие"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Выбранная папка не пуста. При установке существующие данные могут быть удалены. Рекомендуется выбрать другую папку."), ].iter().cloned().collect(); } diff --git a/src/lang/sc.rs b/src/lang/sc.rs index 166f9c262..ced590ed6 100644 --- a/src/lang/sc.rs +++ b/src/lang/sc.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Seletziona Android → media."), ("Trust duration (days)", "Durada de sa fidùtzia (dies)"), ("Renew trust", "Annoa sa fidùtzia"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Sa cartella seletzionada non est bòida. S'installatzione diat pòdere iscantzellare sos datos chi bi sunt. Ti cussigiamus de isseberare un'àtera cartella."), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 75113960d..1fa81c6aa 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vyberte Android → media."), ("Trust duration (days)", "Doba dôvery (dni)"), ("Renew trust", "Obnoviť dôveru"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Vybraný priečinok nie je prázdny. Inštalácia môže odstrániť existujúce údaje. Zvážte výber iného priečinka."), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index a15c02f8e..004f32266 100644 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Izberite Android → media."), ("Trust duration (days)", "Trajanje zaupanja (dni)"), ("Renew trust", "Obnovi zaupanje"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Izbrana mapa ni prazna. Namestitev lahko izbriše obstoječe podatke. Razmislite o izbiri druge mape."), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 69669749b..cda45767d 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Ju lutemi zgjidhni Android → media."), ("Trust duration (days)", "Kohëzgjatja e besimit (ditë)"), ("Renew trust", "Rinovo besimin"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Dosja e zgjedhur nuk është bosh. Instalimi mund të fshijë të dhënat ekzistuese. Konsideroni të zgjidhni një dosje tjetër."), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 6e1d73783..7436d095b 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Izaberite Android → media."), ("Trust duration (days)", "Trajanje poverenja (dani)"), ("Renew trust", "Obnovi poverenje"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Izabrani direktorijum nije prazan. Instalacija može da obriše postojeće podatke. Razmislite o izboru drugog direktorijuma."), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 49c7e328b..47a8a24b9 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Välj Android → media."), ("Trust duration (days)", "Förtroendeperiod (dagar)"), ("Renew trust", "Förnya förtroende"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Den valda mappen är inte tom. Installationen kan radera befintliga data. Överväg att välja en annan mapp."), ].iter().cloned().collect(); } diff --git a/src/lang/ta.rs b/src/lang/ta.rs index de48cc36e..d49385fd4 100644 --- a/src/lang/ta.rs +++ b/src/lang/ta.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Android → media என்பதைத் தேர்ந்தெடுக்கவும்."), ("Trust duration (days)", "நம்பிக்கைக் காலம் (நாட்கள்)"), ("Renew trust", "நம்பிக்கையைப் புதுப்பி"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "தேர்ந்தெடுக்கப்பட்ட கோப்புறை காலியாக இல்லை. நிறுவல் ஏற்கனவே உள்ள தரவை நீக்கக்கூடும். வேறு கோப்புறையைத் தேர்ந்தெடுப்பது நல்லது."), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 736f33bd5..bb8e2210a 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "กรุณาเลือก Android → media"), ("Trust duration (days)", "ระยะเวลาการเชื่อถือ (วัน)"), ("Renew trust", "ต่ออายุการเชื่อถือ"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "โฟลเดอร์ที่เลือกไม่ว่างเปล่า การติดตั้งอาจลบข้อมูลที่มีอยู่ ควรพิจารณาเลือกโฟลเดอร์อื่น"), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index b90e4ad0a..eb2df49cc 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Lütfen Android → media konumunu seçin."), ("Trust duration (days)", "Güven süresi (gün)"), ("Renew trust", "Güveni yenile"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Seçilen klasör boş değil. Kurulum mevcut verileri silebilir. Farklı bir klasör seçmeyi düşünün."), ].iter().cloned().collect(); } diff --git a/src/lang/uk.rs b/src/lang/uk.rs index b86cfb7bd..d37376e3c 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Будь ласка, виберіть Android → media."), ("Trust duration (days)", "Термін довіри (у днях)"), ("Renew trust", "Подовжити довіру"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Вибрана тека не порожня. Встановлення може видалити наявні дані. Радимо вибрати іншу теку."), ].iter().cloned().collect(); } diff --git a/src/lang/ur.rs b/src/lang/ur.rs index 0cfaf2543..11e81d905 100644 --- a/src/lang/ur.rs +++ b/src/lang/ur.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "براہ کرم Android → media منتخب کریں۔"), ("Trust duration (days)", "اعتماد کی مدت (دن)"), ("Renew trust", "اعتماد کی تجدید کریں"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "منتخب فولڈر خالی نہیں ہے۔ انسٹالیشن موجودہ ڈیٹا کو حذف کر سکتی ہے۔ کوئی دوسرا فولڈر منتخب کرنے پر غور کریں۔"), ].iter().cloned().collect(); } diff --git a/src/lang/vi.rs b/src/lang/vi.rs index af8e4464e..b7eecab5f 100644 --- a/src/lang/vi.rs +++ b/src/lang/vi.rs @@ -823,6 +823,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please select Android → media.", "Vui lòng chọn Android → media."), ("Trust duration (days)", "Thời hạn tin cậy (ngày)"), ("Renew trust", "Gia hạn tin cậy"), - ("install-directory-not-empty-tip", ""), + ("install-directory-not-empty-tip", "Thư mục đã chọn không trống. Quá trình cài đặt có thể xóa dữ liệu hiện có. Hãy cân nhắc chọn một thư mục khác."), ].iter().cloned().collect(); }