mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba414c4446 | ||
|
|
5b7f17bdaf | ||
|
|
73068f5868 | ||
|
|
b093010b64 | ||
|
|
6f2b4bb80a | ||
|
|
a617b2b410 | ||
|
|
bdbfc0bfd4 | ||
|
|
e9577f953f |
No files matched your search
+235
-10
@@ -1467,7 +1467,11 @@ fn cmd_sync_restore(yes: bool, device: Option<String>) -> Result<()> {
|
||||
items: chosen,
|
||||
},
|
||||
None => CtlRequest::RestoreSyncFolders { items: chosen },
|
||||
})?)
|
||||
})?)?;
|
||||
// The daemon acks before it works, so the outcome, and any folder it had
|
||||
// to skip, only shows up later.
|
||||
println!("Run `pdfs activity` to see which folders were restored.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Expand a leading `~` in a hand-typed path. The daemon requires an absolute
|
||||
@@ -2384,12 +2388,21 @@ where
|
||||
/// an external unmount triggers a remount; errors back off and retry. This is
|
||||
/// the entry point for the systemd user service.
|
||||
fn cmd_daemon(mountpoint: Option<PathBuf>) -> Result<()> {
|
||||
let state_dir = AppDirs::new()?.state_dir();
|
||||
// A failure an earlier daemon recorded is not this one's.
|
||||
pdfs_core::mountfail::clear(&state_dir);
|
||||
let mut failure: Option<pdfs_core::mountfail::MountFailure> = None;
|
||||
loop {
|
||||
// Wait until a session is stored. The GUI enables this service on login,
|
||||
// but the service may also start at boot before the user has logged in.
|
||||
wait_for_session(auth::ready, pdfs_fuse::idle_sleep)?;
|
||||
|
||||
match mount_once(mountpoint.clone()) {
|
||||
let outcome = mount_once(mountpoint.clone());
|
||||
if outcome.is_ok() {
|
||||
// The mount came up, so the next failure starts a new run of them.
|
||||
failure = None;
|
||||
}
|
||||
match outcome {
|
||||
Ok(pdfs_fuse::MountOutcome::Shutdown) => {
|
||||
tracing::info!("daemon stopping");
|
||||
return Ok(());
|
||||
@@ -2404,13 +2417,51 @@ fn cmd_daemon(mountpoint: Option<PathBuf>) -> Result<()> {
|
||||
Err(e) => {
|
||||
// `{:#}` so the whole anyhow chain lands in the log — the top
|
||||
// context alone ("mount failed") never says *why*.
|
||||
tracing::error!(error = format!("{e:#}"), "mount failed; retrying in 5s");
|
||||
pdfs_fuse::idle_sleep(std::time::Duration::from_secs(5));
|
||||
let error = format!("{e:#}");
|
||||
let now = unix_now();
|
||||
let attempts = failure.as_ref().map_or(1, |f| f.attempts.saturating_add(1));
|
||||
let delay = mount_retry_delay(attempts);
|
||||
tracing::error!(
|
||||
error,
|
||||
attempts,
|
||||
retry_secs = delay.as_secs(),
|
||||
"mount failed; retrying"
|
||||
);
|
||||
let record = pdfs_core::mountfail::MountFailure {
|
||||
error: error.clone(),
|
||||
since: failure.as_ref().map_or(now, |f| f.since),
|
||||
attempts,
|
||||
next_attempt: now + delay.as_secs() as i64,
|
||||
};
|
||||
if let Err(e) = pdfs_core::mountfail::record(&state_dir, &record) {
|
||||
tracing::warn!(error = %e, "could not record the mount failure");
|
||||
}
|
||||
pdfs_fuse::notify_status(&format!("Mount failing: {error}"));
|
||||
failure = Some(record);
|
||||
pdfs_fuse::idle_sleep(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How long to wait before mount attempt `attempts + 1`, after `attempts`
|
||||
/// failures in a row.
|
||||
///
|
||||
/// Doubles from 5 s to a 2-minute ceiling. A flat 5 s retry sent about four
|
||||
/// requests to Proton per attempt, tens of thousands a day from an account
|
||||
/// whose mount fails on a key, which no retry fixes (`docs/BUGS.md` B263). The
|
||||
/// ceiling stays at minutes because a cold start without a network fails the
|
||||
/// same way, and must mount soon after the network returns.
|
||||
fn mount_retry_delay(attempts: u32) -> std::time::Duration {
|
||||
const BASE_SECS: u64 = 5;
|
||||
const MAX_SECS: u64 = 120;
|
||||
|
||||
let factor = 1u64
|
||||
.checked_shl(attempts.saturating_sub(1))
|
||||
.unwrap_or(u64::MAX);
|
||||
std::time::Duration::from_secs(BASE_SECS.saturating_mul(factor).min(MAX_SECS))
|
||||
}
|
||||
|
||||
/// One mount run: resume the session, mount, and block until the mount ends
|
||||
/// (clean shutdown or external unmount). Returns the [`MountOutcome`] so the
|
||||
/// daemon loop can decide whether to exit or remount.
|
||||
@@ -3399,6 +3450,79 @@ fn cmd_sync_limit(up: Option<u64>, down: Option<u64>) -> Result<()> {
|
||||
})?)
|
||||
}
|
||||
|
||||
/// How long the oldest write may wait to upload before `diagnose` calls it
|
||||
/// out. Under a day it is a slow link or a big file; past it, something is
|
||||
/// holding the queue.
|
||||
const STAGED_STALE_SECS: u64 = 86_400;
|
||||
|
||||
/// `diagnose` findings for the queue and the pause, from the `Status` reply.
|
||||
/// `paused_for` is how long a timed pause still runs, in seconds.
|
||||
fn sync_health(
|
||||
failing_ops: u64,
|
||||
failing_error: Option<&str>,
|
||||
staged_bytes: u64,
|
||||
staged_oldest_secs: u64,
|
||||
paused: bool,
|
||||
paused_for: Option<i64>,
|
||||
) -> Vec<(DiagnoseLevel, &'static str, String)> {
|
||||
let mut findings = Vec::new();
|
||||
if failing_ops > 0 {
|
||||
findings.push((
|
||||
DiagnoseLevel::Warn,
|
||||
" failing writes",
|
||||
match failing_error {
|
||||
Some(error) => format!("{failing_ops} keep failing; the last error: {error}"),
|
||||
None => format!("{failing_ops} keep failing"),
|
||||
},
|
||||
));
|
||||
}
|
||||
if staged_bytes > 0 && staged_oldest_secs >= STAGED_STALE_SECS {
|
||||
findings.push((
|
||||
DiagnoseLevel::Warn,
|
||||
" oldest unsent write",
|
||||
format!(
|
||||
"{} old, {} only on this computer",
|
||||
human_secs(staged_oldest_secs),
|
||||
human_bytes(staged_bytes)
|
||||
),
|
||||
));
|
||||
}
|
||||
if paused {
|
||||
findings.push((
|
||||
DiagnoseLevel::Warn,
|
||||
" sync",
|
||||
match paused_for {
|
||||
Some(left) if left > 0 => {
|
||||
format!("paused for another {}", human_secs(left as u64))
|
||||
}
|
||||
_ => "paused until resumed (`pdfs sync resume`)".to_string(),
|
||||
},
|
||||
));
|
||||
}
|
||||
findings
|
||||
}
|
||||
|
||||
/// `diagnose` findings for the synced folders that need someone to look.
|
||||
fn folder_health(
|
||||
folders: &[pdfs_core::control::SyncFolderInfo],
|
||||
) -> Vec<(DiagnoseLevel, String, String)> {
|
||||
folders
|
||||
.iter()
|
||||
.filter_map(|folder| {
|
||||
let what = match folder.state.as_str() {
|
||||
"error" => "sync error; `pdfs sync list` and the activity feed say more",
|
||||
"conflict" => "conflict copies to look at",
|
||||
_ => return None,
|
||||
};
|
||||
Some((
|
||||
DiagnoseLevel::Warn,
|
||||
format!(" synced folder [{}]", folder.id),
|
||||
format!("{}: {what}", folder.local_path),
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn unix_now() -> i64 {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
@@ -3735,21 +3859,26 @@ fn cmd_diagnose() -> Result<()> {
|
||||
let socket_exists = socket.exists();
|
||||
let socket_kind_ok =
|
||||
std::fs::symlink_metadata(&socket).is_ok_and(|m| m.file_type().is_socket());
|
||||
// A stale socket file with no listener is the common failure, so the reply
|
||||
// is what decides this rather than the file's existence.
|
||||
let status = control_request(CtlRequest::Status);
|
||||
report.finding(
|
||||
if socket_exists && !socket_kind_ok {
|
||||
DiagnoseLevel::Fail
|
||||
} else if socket_exists {
|
||||
} else if socket_exists && status.is_ok() {
|
||||
DiagnoseLevel::Ok
|
||||
} else {
|
||||
DiagnoseLevel::Warn
|
||||
},
|
||||
" control socket",
|
||||
socket.display().to_string(),
|
||||
if socket_exists && socket_kind_ok && status.is_err() {
|
||||
format!("{} (left over; nothing listens on it)", socket.display())
|
||||
} else {
|
||||
socket.display().to_string()
|
||||
},
|
||||
);
|
||||
// A stale socket file with no listener is the common failure, so the reply
|
||||
// is what decides this rather than the file's existence.
|
||||
let mut daemon_responding = false;
|
||||
match control_request(CtlRequest::Status) {
|
||||
match status {
|
||||
Ok(CtlResponse::Status {
|
||||
mountpoint,
|
||||
online,
|
||||
@@ -3757,6 +3886,12 @@ fn cmd_diagnose() -> Result<()> {
|
||||
tokens_unsaved,
|
||||
pending_uploads,
|
||||
pending_changes,
|
||||
failing_ops,
|
||||
failing_error,
|
||||
staged_bytes,
|
||||
staged_oldest_secs,
|
||||
paused,
|
||||
paused_until,
|
||||
version,
|
||||
..
|
||||
}) => {
|
||||
@@ -3831,6 +3966,27 @@ fn cmd_diagnose() -> Result<()> {
|
||||
format!("{pending} waiting to upload")
|
||||
},
|
||||
);
|
||||
// The rest of the daemon's state that the report used to drop:
|
||||
// writes that keep failing, a pause, a folder in error. Each of
|
||||
// them stops changes from reaching Drive while everything above
|
||||
// reads as fine (`docs/BUGS.md` B268).
|
||||
for (level, check, detail) in sync_health(
|
||||
failing_ops,
|
||||
failing_error.as_deref(),
|
||||
staged_bytes,
|
||||
staged_oldest_secs,
|
||||
paused,
|
||||
paused_until.map(|until| until - unix_now()),
|
||||
) {
|
||||
report.finding(level, check, detail);
|
||||
}
|
||||
if let Ok(CtlResponse::SyncFolders { items }) =
|
||||
control_request(CtlRequest::ListSyncFolders)
|
||||
{
|
||||
for (level, check, detail) in folder_health(&items) {
|
||||
report.finding(level, &check, detail);
|
||||
}
|
||||
}
|
||||
match await_integrity_check() {
|
||||
Ok(CtlResponse::CacheReport {
|
||||
integrity_problems,
|
||||
@@ -3887,7 +4043,23 @@ fn cmd_diagnose() -> Result<()> {
|
||||
" daemon responding",
|
||||
format!("stopped or unreachable: {e:#}"),
|
||||
);
|
||||
println!(" (start it with `systemctl --user start proton-drive.service`)");
|
||||
match pdfs_core::mountfail::read(&dirs.state_dir()) {
|
||||
// A daemon whose mount keeps failing never opens the socket,
|
||||
// so this is the only place its reason shows (B263).
|
||||
Some(failure) => report.finding(
|
||||
DiagnoseLevel::Fail,
|
||||
" mount",
|
||||
format!(
|
||||
"failing for {}, {} attempt(s): {}",
|
||||
human_secs(unix_now().saturating_sub(failure.since).max(0) as u64),
|
||||
failure.attempts,
|
||||
failure.error
|
||||
),
|
||||
),
|
||||
None => println!(
|
||||
" (start it with `systemctl --user start proton-drive.service`)"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4012,6 +4184,50 @@ mod pause_tests {
|
||||
mod diagnose_tests {
|
||||
use super::*;
|
||||
|
||||
/// A healthy queue says nothing; writes that keep failing, an old unsent
|
||||
/// write and a pause each say so (`docs/BUGS.md` B268).
|
||||
#[test]
|
||||
fn a_stuck_queue_or_a_pause_is_reported() {
|
||||
assert!(sync_health(0, None, 0, 0, false, None).is_empty());
|
||||
assert!(sync_health(0, None, 4096, STAGED_STALE_SECS - 1, false, None).is_empty());
|
||||
let checks: Vec<&str> = sync_health(
|
||||
2,
|
||||
Some("upload a.txt: quota"),
|
||||
4096,
|
||||
STAGED_STALE_SECS,
|
||||
true,
|
||||
Some(600),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(level, check, _)| {
|
||||
assert!(matches!(level, DiagnoseLevel::Warn));
|
||||
check.trim()
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(checks, ["failing writes", "oldest unsent write", "sync"]);
|
||||
}
|
||||
|
||||
/// Issue #53: a folder that would not sync, and `diagnose` found nothing.
|
||||
#[test]
|
||||
fn a_synced_folder_in_error_is_reported() {
|
||||
let folder = |id: i64, state: &str| pdfs_core::control::SyncFolderInfo {
|
||||
id,
|
||||
local_path: format!("/home/u/f{id}"),
|
||||
remote_uid: "vol~link".into(),
|
||||
mode: "mirror".into(),
|
||||
state: state.into(),
|
||||
last_sync: 0,
|
||||
pending_mode: None,
|
||||
progress: None,
|
||||
paused: false,
|
||||
};
|
||||
let findings =
|
||||
folder_health(&[folder(1, "idle"), folder(2, "error"), folder(3, "syncing")]);
|
||||
assert_eq!(findings.len(), 1);
|
||||
assert_eq!(findings[0].1.trim(), "synced folder [2]");
|
||||
assert!(findings[0].2.starts_with("/home/u/f2: sync error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finding_severity_is_counted_centrally() {
|
||||
let mut report = DiagnoseReport::default();
|
||||
@@ -4047,6 +4263,15 @@ mod diagnose_tests {
|
||||
#[cfg(test)]
|
||||
mod login_wait_tests {
|
||||
use super::*;
|
||||
|
||||
/// The mount retry backs off from 5 s to a ceiling of minutes, rather than
|
||||
/// asking Proton every 5 s forever (`docs/BUGS.md` B263).
|
||||
#[test]
|
||||
fn a_failing_mount_backs_off_to_a_ceiling() {
|
||||
let secs: Vec<u64> = (1..=8).map(|n| mount_retry_delay(n).as_secs()).collect();
|
||||
assert_eq!(secs, [5, 10, 20, 40, 80, 120, 120, 120]);
|
||||
assert_eq!(mount_retry_delay(u32::MAX).as_secs(), 120);
|
||||
}
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -160,13 +160,14 @@ impl CoreError {
|
||||
/// telling the user their connection is down beats telling them the
|
||||
/// server refused. The SDK's other failures say what went wrong instead
|
||||
/// (see [`proton_kind`]).
|
||||
///
|
||||
/// Callers also hand it this crate's [`Error`] from the cache and the
|
||||
/// database, which is not a transport error at all. Taking those for
|
||||
/// "offline" told the user their connection was down when a pin failed on
|
||||
/// the database (`docs/BUGS.md` B266), so the source chain is searched for
|
||||
/// either type before falling back.
|
||||
pub fn from_api(e: &(dyn std::error::Error + 'static), what: &str) -> Self {
|
||||
let kind = match e.downcast_ref::<proton_sdk::ProtonError>() {
|
||||
Some(proton) => proton_kind(proton),
|
||||
// Not the SDK's at all: a timeout around the call, or the socket.
|
||||
None => ErrorKind::Offline,
|
||||
};
|
||||
Self::new(kind, format!("{what}: {e}"))
|
||||
Self::new(api_kind_of(e), format!("{what}: {e}"))
|
||||
}
|
||||
|
||||
/// Prefix the message while keeping the classification.
|
||||
@@ -208,19 +209,40 @@ impl From<serde_json::Error> for CoreError {
|
||||
|
||||
impl From<Error> for CoreError {
|
||||
fn from(e: Error) -> Self {
|
||||
let kind = match &e {
|
||||
Error::NotLoggedIn
|
||||
| Error::ReloginRequired
|
||||
| Error::Locked
|
||||
| Error::WrongMailboxPassword
|
||||
| Error::HumanVerificationRequired(_) => ErrorKind::Denied,
|
||||
Error::Proton(proton) => proton_kind(proton),
|
||||
_ => ErrorKind::Internal,
|
||||
};
|
||||
Self::new(kind, e.to_string())
|
||||
Self::new(core_kind(&e), e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// What this crate's [`Error`] means to a front-end.
|
||||
fn core_kind(e: &Error) -> ErrorKind {
|
||||
match e {
|
||||
Error::NotLoggedIn
|
||||
| Error::ReloginRequired
|
||||
| Error::Locked
|
||||
| Error::WrongMailboxPassword
|
||||
| Error::HumanVerificationRequired(_) => ErrorKind::Denied,
|
||||
Error::Proton(proton) => proton_kind(proton),
|
||||
_ => ErrorKind::Internal,
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind for [`CoreError::from_api`]: the first SDK or crate error in `e`'s
|
||||
/// source chain decides it.
|
||||
fn api_kind_of(e: &(dyn std::error::Error + 'static)) -> ErrorKind {
|
||||
let mut cause = Some(e);
|
||||
while let Some(err) = cause {
|
||||
if let Some(proton) = err.downcast_ref::<proton_sdk::ProtonError>() {
|
||||
return proton_kind(proton);
|
||||
}
|
||||
if let Some(core) = err.downcast_ref::<Error>() {
|
||||
return core_kind(core);
|
||||
}
|
||||
cause = err.source();
|
||||
}
|
||||
// Not the SDK's at all: a timeout around the call, or the socket.
|
||||
ErrorKind::Offline
|
||||
}
|
||||
|
||||
/// What a failed SDK call means to a front-end.
|
||||
///
|
||||
/// Only a transport failure is the connection. The rest happened on this
|
||||
@@ -414,6 +436,20 @@ mod tests {
|
||||
assert_ne!(e.kind, ErrorKind::Offline);
|
||||
}
|
||||
|
||||
/// A pin or a search that fails on the database is not the connection, so
|
||||
/// it must not say "You're offline" (`docs/BUGS.md` B266). An SDK error
|
||||
/// wrapped in this crate's type classifies as the SDK error does.
|
||||
#[test]
|
||||
fn a_database_error_handed_to_from_api_is_not_offline() {
|
||||
let db = Error::Other("database is locked".into());
|
||||
assert_eq!(CoreError::from_api(&db, "pin").kind, ErrorKind::Internal);
|
||||
let wrapped = Error::Proton(api(C::DoesNotExist));
|
||||
assert_eq!(
|
||||
CoreError::from_api(&wrapped, "load node").kind,
|
||||
ErrorKind::NotFound
|
||||
);
|
||||
}
|
||||
|
||||
/// `Error` is the plumbing type; converting it must classify as sharply as
|
||||
/// the direct path, or the two disagree about the same failure.
|
||||
#[test]
|
||||
|
||||
@@ -15,6 +15,7 @@ pub mod kernelkey;
|
||||
pub mod keycheck;
|
||||
pub mod localindex;
|
||||
pub mod menu;
|
||||
pub mod mountfail;
|
||||
pub mod mounts;
|
||||
pub mod opener;
|
||||
pub mod places;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//! Why the daemon's mount keeps failing, for the front ends to read.
|
||||
//!
|
||||
//! The control socket opens only once a mount has succeeded, so a daemon whose
|
||||
//! mount fails on every try had no way to say why. The app and the tray showed
|
||||
//! "Connecting…" for days while the reason sat in the journal (`docs/BUGS.md`
|
||||
//! B263). The daemon now writes its last failure to a small file in the state
|
||||
//! directory, next to the control socket, and removes it once a mount is up.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The file's name inside the state directory.
|
||||
const FILE_NAME: &str = "mount-failure.json";
|
||||
|
||||
/// The last failed mount attempt.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MountFailure {
|
||||
/// The error, in English, as the journal has it.
|
||||
pub error: String,
|
||||
/// Unix seconds of the first failure in this run of failures.
|
||||
pub since: i64,
|
||||
/// Failed attempts in a row.
|
||||
pub attempts: u32,
|
||||
/// Unix seconds of the next attempt.
|
||||
pub next_attempt: i64,
|
||||
}
|
||||
|
||||
/// Where the failure is kept for the state directory `state_dir`.
|
||||
pub fn path(state_dir: &Path) -> PathBuf {
|
||||
state_dir.join(FILE_NAME)
|
||||
}
|
||||
|
||||
/// Record `failure`, replacing the one before it. Written to a temporary file
|
||||
/// and renamed, so a reader never sees half of it.
|
||||
pub fn record(state_dir: &Path, failure: &MountFailure) -> std::io::Result<()> {
|
||||
let json = serde_json::to_vec(failure).map_err(std::io::Error::other)?;
|
||||
let tmp = state_dir.join(format!("{FILE_NAME}.tmp"));
|
||||
std::fs::write(&tmp, json)?;
|
||||
std::fs::rename(&tmp, path(state_dir))
|
||||
}
|
||||
|
||||
/// Forget the failure: a mount is up, or a new daemon has started.
|
||||
pub fn clear(state_dir: &Path) {
|
||||
let _ = std::fs::remove_file(path(state_dir));
|
||||
}
|
||||
|
||||
/// The recorded failure, if there is one and it can be read.
|
||||
pub fn read(state_dir: &Path) -> Option<MountFailure> {
|
||||
let bytes = std::fs::read(path(state_dir)).ok()?;
|
||||
serde_json::from_slice(&bytes).ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_recorded_failure_reads_back_until_cleared() {
|
||||
let dir = std::env::temp_dir().join(format!("pdfs-mountfail-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
assert_eq!(read(&dir), None);
|
||||
let failure = MountFailure {
|
||||
error: "fetch My Files root: missing key".into(),
|
||||
since: 1_700_000_000,
|
||||
attempts: 3,
|
||||
next_attempt: 1_700_000_040,
|
||||
};
|
||||
record(&dir, &failure).unwrap();
|
||||
assert_eq!(read(&dir), Some(failure));
|
||||
clear(&dir);
|
||||
assert_eq!(read(&dir), None);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
@@ -1526,9 +1526,17 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
|
||||
let core = core.clone();
|
||||
std::thread::spawn(move || {
|
||||
match core.restore_sync_folders(&items, device.as_deref()) {
|
||||
Ok(message) => {
|
||||
Ok(outcome) => {
|
||||
core.touch_profile();
|
||||
core.log_activity(ActivityKind::Download, &message, "", true);
|
||||
if !outcome.complete() {
|
||||
warn!(skipped = ?outcome.skipped, "restore skipped folders");
|
||||
}
|
||||
core.log_activity(
|
||||
ActivityKind::Download,
|
||||
outcome.summary(),
|
||||
outcome.skipped.join("; "),
|
||||
outcome.complete(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(error = %e, "restore failed");
|
||||
|
||||
@@ -124,7 +124,7 @@ use mount::{
|
||||
};
|
||||
use reads::{BlockFlight, BlockRing, PREFETCH_BUDGET, Prefetch, ReaderSlot, STREAM_BYPASS_MIN};
|
||||
use state::{Entry, Intervals, PendingRevision, State, StateGuard, WriteHandle};
|
||||
pub use systemd::idle_sleep;
|
||||
pub use systemd::{idle_sleep, status as notify_status};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use transfers::{CountingWriter, JobGuard, OwnedCountingReader, TransferRegistry};
|
||||
use r#virtual::{
|
||||
|
||||
@@ -541,12 +541,11 @@ pub(crate) fn mount_with(
|
||||
// and sweeps all operating on a mount that no longer exists (`docs/BUGS.md` B44).
|
||||
let mut workers: Vec<std::thread::JoinHandle<()>> = Vec::new();
|
||||
for worker in 0..DRAIN_WORKERS {
|
||||
let core = core.clone();
|
||||
workers.push(
|
||||
std::thread::Builder::new()
|
||||
.name(format!("pdfs-drain-{worker}"))
|
||||
.spawn(move || core.run_pending_drain(worker == 0))?,
|
||||
);
|
||||
let drain_core = core.clone();
|
||||
let spawned = std::thread::Builder::new()
|
||||
.name(format!("pdfs-drain-{worker}"))
|
||||
.spawn(move || drain_core.run_pending_drain(worker == 0));
|
||||
keep_or_abandon(&core, control_socket, &mut workers, spawned)?;
|
||||
}
|
||||
|
||||
// Start the folder-sync engine. It watches every mirror folder, polls the
|
||||
@@ -566,24 +565,22 @@ pub(crate) fn mount_with(
|
||||
info!("conflict sweep disabled by configuration");
|
||||
} else {
|
||||
info!(mode = ?sweep_mode, "conflict sweep enabled");
|
||||
let core = core.clone();
|
||||
workers.push(
|
||||
std::thread::Builder::new()
|
||||
.name("pdfs-conflict-sweep".into())
|
||||
.spawn(move || core.run_conflict_sweep_loop())?,
|
||||
);
|
||||
let sweep_core = core.clone();
|
||||
let spawned = std::thread::Builder::new()
|
||||
.name("pdfs-conflict-sweep".into())
|
||||
.spawn(move || sweep_core.run_conflict_sweep_loop());
|
||||
keep_or_abandon(&core, control_socket, &mut workers, spawned)?;
|
||||
}
|
||||
|
||||
// Watch the link for the life of the mount: a mount from the cache waits for
|
||||
// the network to come back, and an online one can lose it at any moment and
|
||||
// must notice it coming back just the same.
|
||||
{
|
||||
let core = core.clone();
|
||||
workers.push(
|
||||
std::thread::Builder::new()
|
||||
.name("pdfs-online-probe".into())
|
||||
.spawn(move || core.run_online_probe())?,
|
||||
);
|
||||
let probe_core = core.clone();
|
||||
let spawned = std::thread::Builder::new()
|
||||
.name("pdfs-online-probe".into())
|
||||
.spawn(move || probe_core.run_online_probe());
|
||||
keep_or_abandon(&core, control_socket, &mut workers, spawned)?;
|
||||
}
|
||||
|
||||
// Keep the launcher prompt's "This computer" index fresh. Its own thread:
|
||||
@@ -594,11 +591,10 @@ pub(crate) fn mount_with(
|
||||
let transfers = core.transfers.clone();
|
||||
let mountpoint = mountpoint.to_path_buf();
|
||||
let shutdown = core.shutdown.clone();
|
||||
workers.push(
|
||||
std::thread::Builder::new()
|
||||
.name("pdfs-localindex".into())
|
||||
.spawn(move || run_local_index(db, indexing, transfers, mountpoint, shutdown))?,
|
||||
);
|
||||
let spawned = std::thread::Builder::new()
|
||||
.name("pdfs-localindex".into())
|
||||
.spawn(move || run_local_index(db, indexing, transfers, mountpoint, shutdown));
|
||||
keep_or_abandon(&core, control_socket, &mut workers, spawned)?;
|
||||
}
|
||||
|
||||
// Bind the control socket before the FUSE session takes over the thread. A
|
||||
@@ -607,12 +603,18 @@ pub(crate) fn mount_with(
|
||||
let old_umask = unsafe { libc::umask(0o77) };
|
||||
let listener_res = UnixListener::bind(control_socket);
|
||||
unsafe { libc::umask(old_umask) };
|
||||
let listener = listener_res?;
|
||||
let listener = match listener_res {
|
||||
Ok(listener) => listener,
|
||||
Err(error) => {
|
||||
abandon_workers(&core, control_socket, workers);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
// Owner-only before anything can connect: a peer on this socket commands the
|
||||
// daemon's authenticated session without a credential of its own (B6).
|
||||
if let Err(e) = pdfs_core::config::restrict_socket(control_socket) {
|
||||
error!(error = %e, "cannot restrict control socket permissions; refusing to serve");
|
||||
let _ = std::fs::remove_file(control_socket);
|
||||
abandon_workers(&core, control_socket, workers);
|
||||
return Err(std::io::Error::other(format!(
|
||||
"control socket permissions: {e}"
|
||||
)));
|
||||
@@ -623,7 +625,7 @@ pub(crate) fn mount_with(
|
||||
let bg = match spawn_session(&core, mountpoint, root) {
|
||||
Ok(session) => session,
|
||||
Err(error) => {
|
||||
let _ = std::fs::remove_file(control_socket);
|
||||
abandon_workers(&core, control_socket, workers);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
@@ -643,7 +645,7 @@ pub(crate) fn mount_with(
|
||||
Err(error) => {
|
||||
// A failure here abandons the mount, so the background threads
|
||||
// started above have to be told to stop with it.
|
||||
stop_workers(&core, control_socket, workers);
|
||||
abandon_workers(&core, control_socket, workers);
|
||||
let secondaries = core.mounts.lock().close_and_drain();
|
||||
for (id, mount) in secondaries {
|
||||
if let Err(unmount_error) = mount.teardown() {
|
||||
@@ -693,8 +695,12 @@ pub(crate) fn mount_with(
|
||||
}
|
||||
}
|
||||
// The mount is live and the control socket answers: this is the moment the
|
||||
// unit is actually usable, which is what `Type=notify` reports.
|
||||
// unit is actually usable, which is what `Type=notify` reports. Whatever
|
||||
// failure an earlier attempt recorded for the front ends is over.
|
||||
systemd::ready();
|
||||
if let Some(state_dir) = control_socket.parent() {
|
||||
pdfs_core::mountfail::clear(state_dir);
|
||||
}
|
||||
|
||||
// Re-establish on-demand mounts only after the primary session is live.
|
||||
// Keep the worker so shutdown can close publication, wait for any in-flight
|
||||
@@ -860,6 +866,43 @@ fn stop_workers(core: &Core, control_socket: &Path, workers: Vec<std::thread::Jo
|
||||
// Say so before the joins: a teardown that takes a while must not be read as
|
||||
// a missed watchdog ping and turned into a kill.
|
||||
systemd::stopping();
|
||||
halt_workers(core, control_socket, workers);
|
||||
}
|
||||
|
||||
/// Stop the threads a mount attempt started before it failed.
|
||||
///
|
||||
/// The early returns used to leave them running: every retry started another
|
||||
/// set beside the last one's, and their `Core` kept the database open, so the
|
||||
/// next attempt could fail on its lock instead of the real error
|
||||
/// (`docs/BUGS.md` B264). Unlike [`stop_workers`] this does not tell systemd
|
||||
/// the daemon is stopping, because it is about to try again, and a manager
|
||||
/// told so would wait for the process to exit and then kill it.
|
||||
fn abandon_workers(core: &Core, control_socket: &Path, workers: Vec<std::thread::JoinHandle<()>>) {
|
||||
halt_workers(core, control_socket, workers);
|
||||
}
|
||||
|
||||
/// Keep a worker the mount just spawned, or, if the spawn failed, stop the
|
||||
/// ones already running and hand the error back.
|
||||
fn keep_or_abandon(
|
||||
core: &Core,
|
||||
control_socket: &Path,
|
||||
workers: &mut Vec<std::thread::JoinHandle<()>>,
|
||||
spawned: std::io::Result<std::thread::JoinHandle<()>>,
|
||||
) -> std::io::Result<()> {
|
||||
match spawned {
|
||||
Ok(handle) => {
|
||||
workers.push(handle);
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
abandon_workers(core, control_socket, std::mem::take(workers));
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Signal every worker to stop, then join them up to [`WORKER_JOIN_DEADLINE`].
|
||||
fn halt_workers(core: &Core, control_socket: &Path, workers: Vec<std::thread::JoinHandle<()>>) {
|
||||
core.shutdown.stop();
|
||||
core.wake_drain();
|
||||
let _ = core.sync_tx.send(sync::SyncMsg::Stop);
|
||||
|
||||
@@ -388,7 +388,7 @@ impl Core {
|
||||
&self,
|
||||
items: &[pdfs_core::control::RestoreItem],
|
||||
device: Option<&str>,
|
||||
) -> CoreResult<String> {
|
||||
) -> CoreResult<RestoreOutcome> {
|
||||
if items.is_empty() {
|
||||
return Err(CoreError::invalid("nothing to restore"));
|
||||
}
|
||||
@@ -434,10 +434,19 @@ impl Core {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let id = self
|
||||
.db
|
||||
.sync_folder_add(&local.to_string_lossy(), &item.remote_uid, &device.share_id)
|
||||
.map_err(|e| CoreError::internal(format!("db: {e:?}")))?;
|
||||
let id = match self.db.sync_folder_add(
|
||||
&local.to_string_lossy(),
|
||||
&item.remote_uid,
|
||||
&device.share_id,
|
||||
) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
// The folders restored before this one still need their
|
||||
// watches.
|
||||
let _ = self.sync_tx.send(sync::SyncMsg::Rewatch);
|
||||
return Err(CoreError::internal(format!("db: {e:?}")));
|
||||
}
|
||||
};
|
||||
// `ondemand` is applied through the normal request path, which
|
||||
// mounts the FUSE session and handles the folder being busy — the
|
||||
// restore does not get its own copy of that logic.
|
||||
@@ -454,12 +463,35 @@ impl Core {
|
||||
for reason in &skipped {
|
||||
info!(reason, "restore skipped a folder");
|
||||
}
|
||||
let mut message = format!("restored {restored} folder(s)");
|
||||
if !skipped.is_empty() {
|
||||
message.push_str(&format!("; skipped: {}", skipped.join(", ")));
|
||||
}
|
||||
info!(restored, skipped = skipped.len(), "restore complete");
|
||||
Ok(message)
|
||||
Ok(RestoreOutcome { restored, skipped })
|
||||
}
|
||||
}
|
||||
|
||||
/// What a restore did: how many folders it attached, and why it left out the
|
||||
/// others.
|
||||
pub(crate) struct RestoreOutcome {
|
||||
pub(crate) restored: usize,
|
||||
pub(crate) skipped: Vec<String>,
|
||||
}
|
||||
|
||||
impl RestoreOutcome {
|
||||
/// The activity entry's headline.
|
||||
pub(crate) fn summary(&self) -> String {
|
||||
match (self.restored, self.skipped.len()) {
|
||||
(n, 0) => format!("restored {n} folder(s)"),
|
||||
(0, k) => format!("restored no folders; skipped {k}"),
|
||||
(n, k) => format!("restored {n} folder(s); skipped {k}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the restore did all it was asked. A skipped folder is a
|
||||
/// failure the user has to see: the reply went out as "restoring folders"
|
||||
/// before any work, and a restore that skipped every folder used to be
|
||||
/// logged as a success, so nothing anywhere said that nothing would
|
||||
/// download (`docs/BUGS.md` B269).
|
||||
pub(crate) fn complete(&self) -> bool {
|
||||
self.skipped.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1162,14 +1162,22 @@ impl Core {
|
||||
let uids = self
|
||||
.rt
|
||||
.block_on(self.drive.enumerate_folder_children_node_uids(folder))
|
||||
.map_err(|e| format!("enumerate {folder}: {e}"))?;
|
||||
.map_err(|e| format!("enumerate {folder}: {e}"));
|
||||
let uids = match unlisted_gap(walk, prefix, uids)? {
|
||||
Some(uids) => uids,
|
||||
None => return Ok(()),
|
||||
};
|
||||
if uids.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let nodes = self
|
||||
.rt
|
||||
.block_on(self.drive.enumerate_nodes_light(&uids))
|
||||
.map_err(|e| format!("resolve nodes: {e}"))?;
|
||||
.map_err(|e| format!("resolve nodes: {e}"));
|
||||
let nodes = match unlisted_gap(walk, prefix, nodes)? {
|
||||
Some(nodes) => nodes,
|
||||
None => return Ok(()),
|
||||
};
|
||||
// The SDK leaves out a node it cannot decrypt, and says so only in its
|
||||
// log. Its name went with it, so all this can record is the folder.
|
||||
if nodes.len() < uids.len() {
|
||||
@@ -1213,10 +1221,14 @@ impl Core {
|
||||
let sized: HashMap<String, (i64, Option<String>)> = if stale.is_empty() {
|
||||
HashMap::new()
|
||||
} else {
|
||||
self.rt
|
||||
let full = self
|
||||
.rt
|
||||
.block_on(self.drive.enumerate_nodes(&stale))
|
||||
.map_err(|e| format!("resolve nodes: {e}"))?
|
||||
.iter()
|
||||
.map_err(|e| format!("resolve nodes: {e}"));
|
||||
let Some(full) = unlisted_gap(walk, prefix, full)? else {
|
||||
return Ok(());
|
||||
};
|
||||
full.iter()
|
||||
.map(|n| {
|
||||
(
|
||||
n.uid.to_string(),
|
||||
@@ -2034,6 +2046,34 @@ struct RemoteWalk<'a> {
|
||||
gaps: &'a mut RemoteGaps,
|
||||
}
|
||||
|
||||
/// A listing call's result in [`Core::walk_remote`]. A failure below the root
|
||||
/// records `prefix` as unlisted and returns `None`, so the walk goes on
|
||||
/// without that folder. A failure at the root still fails the pass, since
|
||||
/// nothing would be left to compare.
|
||||
///
|
||||
/// One listing error, after the SDK's own retries, used to fail the whole
|
||||
/// walk. Nothing is applied before the walk ends, so on a link that drops a
|
||||
/// request now and then a large first sync started over from nothing on
|
||||
/// every pass and never got past it (`docs/BUGS.md` B271).
|
||||
fn unlisted_gap<T>(
|
||||
walk: &mut RemoteWalk<'_>,
|
||||
prefix: &str,
|
||||
result: Result<T, String>,
|
||||
) -> Result<Option<T>, PassAbort> {
|
||||
match result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(error) if prefix.is_empty() => Err(error.into()),
|
||||
Err(error) => {
|
||||
warn!(
|
||||
folder = prefix,
|
||||
error, "sync: a folder on Drive could not be listed; leaving it out of this pass"
|
||||
);
|
||||
walk.gaps.unlisted(prefix);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the file at `path` is no longer what the scan saw as `planned`.
|
||||
/// A file that is gone has nothing left to lose, so it has not changed.
|
||||
fn changed_since_scan(path: &Path, planned: &LocalItem) -> bool {
|
||||
|
||||
@@ -236,6 +236,9 @@ pub(super) struct RemoteGaps {
|
||||
short: HashSet<String>,
|
||||
/// Paths seen, but whose entry could not be read.
|
||||
unread: HashSet<String>,
|
||||
/// Folders whose listing failed outright, after the SDK's retries. Nothing
|
||||
/// under one is known, so all of it is left out of the pass (B271).
|
||||
unlisted: HashSet<String>,
|
||||
}
|
||||
|
||||
impl RemoteGaps {
|
||||
@@ -249,8 +252,13 @@ impl RemoteGaps {
|
||||
self.unread.insert(rel);
|
||||
}
|
||||
|
||||
/// The listing of `folder` failed, so nothing under it was walked.
|
||||
pub(super) fn unlisted(&mut self, folder: &str) {
|
||||
self.unlisted.insert(folder.to_string());
|
||||
}
|
||||
|
||||
pub(super) fn is_empty(&self) -> bool {
|
||||
self.short.is_empty() && self.unread.is_empty()
|
||||
self.short.is_empty() && self.unread.is_empty() && self.unlisted.is_empty()
|
||||
}
|
||||
|
||||
/// Whether `rel` may be missing from `remote` only because the walk could
|
||||
@@ -272,7 +280,7 @@ impl RemoteGaps {
|
||||
let mut at = parent_rel(rel);
|
||||
loop {
|
||||
if remote_dirs.contains_key(at) {
|
||||
return self.short.contains(at);
|
||||
return self.short.contains(at) || self.unlisted.contains(at);
|
||||
}
|
||||
if at.is_empty() {
|
||||
return false;
|
||||
@@ -295,6 +303,9 @@ impl RemoteGaps {
|
||||
.iter()
|
||||
.map(|rel| format!("{rel} (cannot be read on Proton Drive)")),
|
||||
)
|
||||
.chain(self.unlisted.iter().map(|rel| {
|
||||
format!("{rel} (could not be listed on Proton Drive; tried again next pass)")
|
||||
}))
|
||||
.collect();
|
||||
lines.sort_unstable();
|
||||
lines
|
||||
@@ -844,6 +855,32 @@ mod file_plan_tests {
|
||||
assert!(gaps.hides("top.txt", &remote, &dirs));
|
||||
}
|
||||
|
||||
/// A folder whose listing failed was walked no further: nothing under it
|
||||
/// reads as deleted on Drive, and a sibling that listed still does
|
||||
/// (`docs/BUGS.md` B271).
|
||||
#[test]
|
||||
fn a_folder_that_failed_to_list_hides_everything_under_it() {
|
||||
let remote = HashMap::from([
|
||||
("docs".to_string(), folder()),
|
||||
("pics".to_string(), folder()),
|
||||
]);
|
||||
let dirs = HashMap::from([
|
||||
(String::new(), uid("root")),
|
||||
("docs".to_string(), uid("docs")),
|
||||
("pics".to_string(), uid("pics")),
|
||||
]);
|
||||
let mut gaps = RemoteGaps::default();
|
||||
gaps.unlisted("docs");
|
||||
assert!(gaps.hides("docs/a.txt", &remote, &dirs));
|
||||
assert!(gaps.hides("docs/sub/deep.txt", &remote, &dirs));
|
||||
assert!(!gaps.hides("docs", &remote, &dirs));
|
||||
assert!(!gaps.hides("pics/gone.jpg", &remote, &dirs));
|
||||
assert_eq!(
|
||||
gaps.report(),
|
||||
["docs (could not be listed on Proton Drive; tried again next pass)"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_file_whose_size_could_not_be_read_is_hidden() {
|
||||
let remote: HashMap<String, RemoteItem> = HashMap::new();
|
||||
|
||||
@@ -64,11 +64,19 @@ fn send_abstract(socket: &UnixDatagram, name: &str, message: &str) -> bool {
|
||||
/// so `systemctl --user start` returns when the mount actually works rather than
|
||||
/// when the process exists.
|
||||
pub(crate) fn ready() {
|
||||
if notify("READY=1\n") {
|
||||
if notify("READY=1\nSTATUS=Mounted\n") {
|
||||
debug!("notified systemd: ready");
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the one-line status `systemctl --user status` shows for the unit, such
|
||||
/// as why the mount keeps failing (`docs/BUGS.md` B263).
|
||||
pub fn status(text: &str) {
|
||||
// A newline would end the `STATUS=` line and start another variable.
|
||||
let line = text.replace('\n', " ");
|
||||
notify(&format!("STATUS={line}\n"));
|
||||
}
|
||||
|
||||
/// Tell the manager the daemon is still healthy.
|
||||
pub(crate) fn watchdog_ping() {
|
||||
let _ = notify("WATCHDOG=1\n");
|
||||
|
||||
@@ -1797,13 +1797,35 @@ pub(crate) fn service_unreachable(
|
||||
);
|
||||
return;
|
||||
}
|
||||
status(
|
||||
&ui,
|
||||
"network-idle-symbolic",
|
||||
&gettext("Connecting…"),
|
||||
&gettext("Waiting for the Proton Drive service to start."),
|
||||
false,
|
||||
);
|
||||
// Running but not answering may be a mount that fails on every try,
|
||||
// which never opens the socket. Its reason is in a file the daemon
|
||||
// keeps for this (B263); "Connecting…" alone looked like a slow start
|
||||
// for as long as it lasted.
|
||||
let state_dir = ui.dirs.state_dir();
|
||||
let failure = gio::spawn_blocking(move || pdfs_core::mountfail::read(&state_dir))
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
match failure {
|
||||
Some(failure) => status(
|
||||
&ui,
|
||||
"dialog-warning-symbolic",
|
||||
&gettext("Can't open your Drive"),
|
||||
// Translators: {error} is an error message, in English.
|
||||
&gettext_f(
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. The last error was: {error}",
|
||||
&[("error", &failure.error)],
|
||||
),
|
||||
true,
|
||||
),
|
||||
None => status(
|
||||
&ui,
|
||||
"network-idle-symbolic",
|
||||
&gettext("Connecting…"),
|
||||
&gettext("Waiting for the Proton Drive service to start."),
|
||||
false,
|
||||
),
|
||||
}
|
||||
glib::timeout_add_local_once(CONNECT_RETRY_INTERVAL, move || {
|
||||
if ui.stack.visible_child_name().as_deref() == Some(page) {
|
||||
reload(&ui);
|
||||
|
||||
@@ -194,6 +194,24 @@ fn poll_state(socket: &Path, default_mountpoint: &Path) -> DriveState {
|
||||
),
|
||||
Phase::Attention,
|
||||
),
|
||||
// A daemon whose mount fails on every try never opens the
|
||||
// socket; the app says why (B263).
|
||||
Ok(s)
|
||||
if socket
|
||||
.parent()
|
||||
.and_then(pdfs_core::mountfail::read)
|
||||
.is_some() =>
|
||||
{
|
||||
(
|
||||
// Translators: {username} is the Proton account name. The
|
||||
// service keeps failing to start; the app shows the error.
|
||||
gettext_f(
|
||||
"Signed in as {username} — can't open your Drive",
|
||||
&[("username", &s.username)],
|
||||
),
|
||||
Phase::Attention,
|
||||
)
|
||||
}
|
||||
Ok(s) => (
|
||||
// Translators: {username} is the Proton account name.
|
||||
gettext_f(
|
||||
|
||||
+67
-26
@@ -69,15 +69,9 @@ original wording.
|
||||
| Entry | Title | What is left |
|
||||
|---|---|---|
|
||||
| [B273](#b273--a-restore-proposes-another-computers-paths-when-the-two-share-a-hostname) | A restore proposes another computer's paths when the two share a hostname | Not fixed |
|
||||
| [B271](#b271--one-failed-listing-makes-a-large-first-sync-start-over-from-nothing) | One failed listing makes a large first sync start over from nothing | Not fixed |
|
||||
| [B270](#b270--one-file-left-in-a-mirror-folder-clears-the-wipe-guard-for-all-the-others) | One file left in a mirror folder clears the wipe guard for all the others | Not fixed |
|
||||
| [B269](#b269--a-restore-that-skipped-every-folder-reports-success) | A restore that skipped every folder reports success | Not fixed |
|
||||
| [B268](#b268--pdfs-diagnose-says-no-problems-found-while-sync-is-stuck) | `pdfs diagnose` says "No problems found" while sync is stuck | Not fixed |
|
||||
| [B267](#b267--a-key-failure-at-a-start-from-a-warm-cache-shows-as-offline-for-good) | A key failure at a start from a warm cache shows as offline for good | Not fixed |
|
||||
| [B266](#b266--pin-unpin-search-and-node-loads-report-a-database-error-as-offline) | Pin, unpin, search and node loads report a database error as "offline" | Not fixed |
|
||||
| [B265](#b265--address-keys-whose-token-is-for-an-inactive-account-key-never-open) | Address keys whose token is for an inactive account key never open | Needs an SDK change; not reproduced |
|
||||
| [B264](#b264--a-mount-that-fails-after-starting-its-workers-leaves-them-running) | A mount that fails after starting its workers leaves them running | Not fixed |
|
||||
| [B263](#b263--a-mount-that-can-never-succeed-retries-every-5-s-and-tells-only-the-journal) | A mount that can never succeed retries every 5 s and tells only the journal | Not fixed |
|
||||
| [B134](#b134--a-file-deleted-online-while-the-drain-checks-its-write-is-kept-as-a-conflict-copy) | A file deleted online while the drain checks its write is kept as a conflict copy | Only with `local_first` off |
|
||||
| [B91](#b91--the-daemon-hung-overnight-and-nothing-could-say-what-it-was-waiting-on) | The daemon hung overnight, and nothing could say what it was waiting on | The cause is not diagnosed; the daemon now reports where it is stuck and is restarted |
|
||||
| [B67](#b67--fresh-state-restore-can-silently-select-a-new-device-high-22) | Fresh-state restore can silently select a new device (HIGH-22) | An ambiguous device after a loss of local state is not asked about |
|
||||
@@ -90,6 +84,9 @@ original wording.
|
||||
|
||||
| Entry | Title | What is left |
|
||||
|---|---|---|
|
||||
| [B269](#b269--a-restore-that-skipped-every-folder-reports-success) | A restore that skipped every folder reports success | The app shows the result only in the activity feed; no offer to replace or reset the folder in the way |
|
||||
| [B268](#b268--pdfs-diagnose-says-no-problems-found-while-sync-is-stuck) | `pdfs diagnose` says "No problems found" while sync is stuck | The sync engine's state is not checked |
|
||||
| [B263](#b263--a-mount-that-can-never-succeed-retries-every-5-s-and-tells-only-the-journal) | A mount that can never succeed retries every 5 s and tells only the journal | Errors no retry fixes back off like network errors |
|
||||
| [B262](#b262--watching-a-large-synced-folder-holds-up-the-sync-engine-and-a-failed-watch-is-never-retried) | Watching a large synced folder holds up the sync engine, and a failed watch is never retried | The first pass still waits for the watch; a failed watch is not shown in the app |
|
||||
| [B259](#b259--the-sync-engine-can-die-or-hang-and-nothing-shows-it-until-a-restart) | The sync engine can die or hang, and nothing shows it until a restart | No pass deadline; an ended engine is reported, not restarted; the trigger in #53 is not known |
|
||||
| [B199](#b199--a-stopped-share-stays-on-the-shared-by-me-page-for-minutes) | A stopped share stays on the Shared by me page for minutes | The share itself is not deleted; the SDK has no call for it |
|
||||
@@ -112,6 +109,9 @@ a live account since.
|
||||
|
||||
| Entry | Title |
|
||||
|---|---|
|
||||
| [B271](#b271--one-failed-listing-makes-a-large-first-sync-start-over-from-nothing) | One failed listing makes a large first sync start over from nothing |
|
||||
| [B266](#b266--pin-unpin-search-and-node-loads-report-a-database-error-as-offline) | Pin, unpin, search and node loads report a database error as "offline" |
|
||||
| [B264](#b264--a-mount-that-fails-after-starting-its-workers-leaves-them-running) | A mount that fails after starting its workers leaves them running |
|
||||
| [B261](#b261--a-folder-that-has-never-synced-reads-up-to-date) | A folder that has never synced reads "up to date" |
|
||||
| [B272](#b272--after-a-folder-is-removed-and-added-again-its-file-events-go-to-the-old-id) | After a folder is removed and added again, its file events go to the old id |
|
||||
| [B260](#b260--the-poll-pushes-a-new-folders-first-sync-behind-every-other-folder) | The poll pushes a new folder's first sync behind every other folder |
|
||||
@@ -416,7 +416,7 @@ folder list has now.
|
||||
|
||||
### B271 — One failed listing makes a large first sync start over from nothing
|
||||
|
||||
**Status:** Open
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-31, code audit after issue #46: a folder of about 19,900 items restored on a
|
||||
second computer sat at `scanning…` with nothing downloaded, and the journal showed
|
||||
`RequestTimeout (http 408)`.
|
||||
@@ -433,8 +433,13 @@ the walk is done. Files that did download keep their baseline rows, so only the
|
||||
but on a first sync that is all of it. Progress is also counted only after a folder's full
|
||||
listing, so a large folder shows `scanning…` with 0 checked for a long time.
|
||||
|
||||
**Fix.** Not done. Record a failed subtree as a gap, as undecryptable nodes already are: no
|
||||
deletions under it, and the rest of the pass still applies.
|
||||
**Fix.** A listing that fails below the sync root records that folder as a gap in
|
||||
`RemoteGaps`, next to the folders whose listing came back short. The planner deletes nothing
|
||||
under a gap, the rest of the pass still applies, and the pass report names the folder as
|
||||
tried again next pass. A failed listing of the root itself still fails the pass, since there is
|
||||
nothing left to apply. The progress count inside one large folder is unchanged.
|
||||
|
||||
**Test:** `a_folder_that_failed_to_list_hides_everything_under_it` in `sync::planner`.
|
||||
|
||||
---
|
||||
|
||||
@@ -461,7 +466,7 @@ choice to the user: delete on the other side, or sync from scratch.
|
||||
|
||||
### B269 — A restore that skipped every folder reports success
|
||||
|
||||
**Status:** Open
|
||||
**Status:** Partly fixed
|
||||
**Found:** 2026-10-31, code audit after issue #46: the journal showed `restore complete
|
||||
restored=0 skipped=1` several times, and the user saw nothing.
|
||||
**Where:** the `RestoreSyncFolders` and `RestoreDeviceFolders` handler in
|
||||
@@ -476,15 +481,20 @@ result, `restored 0 folder(s); skipped: …`, is then logged as a successful act
|
||||
reason is only in the activity text and the journal. When the database insert fails partway,
|
||||
the `?` also skips the `Rewatch` for the folders already restored.
|
||||
|
||||
**Fix.** Not done. Log a restore that skipped folders as a failure, name the reason in the app
|
||||
and the CLI, and offer to replace or reset the folder in the way. Send the `Rewatch` on every
|
||||
exit path.
|
||||
**Fix.** `restore_sync_folders` returns what it restored and what it skipped, with the reason
|
||||
for each skip. The daemon logs a restore that skipped any folder as a failed activity, with the
|
||||
reasons as its detail, and warns in the journal. A database error partway now sends the
|
||||
`Rewatch` for the folders already restored before it returns. `pdfs devices join` points to
|
||||
`pdfs activity` for the result.
|
||||
|
||||
Left: the app shows the result only in the activity feed, and nothing offers to replace or reset
|
||||
the folder in the way.
|
||||
|
||||
---
|
||||
|
||||
### B268 — `pdfs diagnose` says "No problems found" while sync is stuck
|
||||
|
||||
**Status:** Open
|
||||
**Status:** Partly fixed. The sync engine's own state is not checked yet.
|
||||
**Found:** 2026-10-31, issue #53: a folder that never synced, and `diagnose` found nothing.
|
||||
**Where:** `diagnose` in `crates/pdfs-cli/src/main.rs`
|
||||
|
||||
@@ -497,8 +507,16 @@ folders or for `Diagnostics`, so a folder in error and a stalled worker are not
|
||||
control socket file with no daemon behind it is also reported `[ok]`; only the next line says
|
||||
the daemon does not answer.
|
||||
|
||||
**Fix.** Not done. Warn on failing or parked writes, a pause, old staged data and folders in
|
||||
error, and report the sync engine's state once B259 exposes it.
|
||||
**Fix.** `diagnose` warns about writes that keep failing (with the last error), a write that
|
||||
has waited a day or more to upload, a pause, and each synced folder in `error` or `conflict`
|
||||
(`sync_health` and `folder_health` in `crates/pdfs-cli/src/main.rs`). A control socket file
|
||||
that nothing listens on is a warning, not `[ok]`. Parked uploads are left out: a file still
|
||||
wearing a download's temporary name is normal. Not done: the sync engine's state, which
|
||||
`Diagnostics` reports once B259's change is in.
|
||||
|
||||
**Test:** `a_stuck_queue_or_a_pause_is_reported` and `a_synced_folder_in_error_is_reported` in
|
||||
`pdfs-cli`. On 2026-10-31 the new `diagnose` against a healthy 3.6.0 daemon still reported
|
||||
"No problems found".
|
||||
|
||||
---
|
||||
|
||||
@@ -525,7 +543,7 @@ report the others as their own state through `Status`.
|
||||
|
||||
### B266 — Pin, unpin, search and node loads report a database error as "offline"
|
||||
|
||||
**Status:** Open
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-31, code audit after issue #38. B205 fixed the photo upload path only.
|
||||
**Where:** `CoreError::from_api` in `crates/pdfs-core/src/error.rs`; its callers in
|
||||
`crates/pdfs-fuse/src/lib.rs` (`pin`, `unpin`, the searches, `load node`)
|
||||
@@ -538,8 +556,11 @@ it was written for SDK calls, where that means a transport error. These callers
|
||||
`pdfs_core::Error` values from the cache and the database. A `ProtonError` wrapped in
|
||||
`pdfs_core::Error::Proton` also misses the downcast, since the source chain is not walked.
|
||||
|
||||
**Fix.** Not done. Use `CoreError::internal` at the database callers, and walk the source
|
||||
chain in `from_api` before falling back.
|
||||
**Fix.** `from_api` walks the source chain. The first `ProtonError` or `pdfs_core::Error` in it
|
||||
decides the kind, the same way `From<Error>` does, and only an error with neither, a transport
|
||||
error, reads as offline. That covers every caller at once, the database ones included.
|
||||
|
||||
**Test:** `a_database_error_handed_to_from_api_is_not_offline` in `pdfs-core`.
|
||||
|
||||
---
|
||||
|
||||
@@ -569,7 +590,7 @@ tokens, and only the active ones for signing. Skip and log an address that canno
|
||||
|
||||
### B264 — A mount that fails after starting its workers leaves them running
|
||||
|
||||
**Status:** Open
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-31, code audit after issue #40.
|
||||
**Where:** `run_mount` in `crates/pdfs-fuse/src/mount.rs`
|
||||
|
||||
@@ -583,14 +604,20 @@ permission check, a `spawn_session` error, and a `?` on a later thread spawn) sk
|
||||
the `flock` held, every later attempt fails with "another daemon is using cache.db" instead
|
||||
of the real error. That part is inferred from the code and not observed.
|
||||
|
||||
**Fix.** Not done. Bind the socket and check the mountpoint before any worker starts, and stop
|
||||
the workers on every error path.
|
||||
**Fix.** Every early return in `mount` after the first worker starts stops the workers
|
||||
(`abandon_workers`, `keep_or_abandon` in `crates/pdfs-fuse/src/mount.rs`). Unlike a normal stop
|
||||
it does not send `STOPPING=1`, because the daemon is about to retry and systemd would
|
||||
otherwise wait for it to exit and then kill it. The control-thread failure path, which already
|
||||
stopped its workers, no longer sends it either.
|
||||
|
||||
**Test:** none of its own; the simulations (`sim::daemon`, 55 passed) cover the normal start
|
||||
and stop.
|
||||
|
||||
---
|
||||
|
||||
### B263 — A mount that can never succeed retries every 5 s and tells only the journal
|
||||
|
||||
**Status:** Open
|
||||
**Status:** Partly fixed. Errors that no retry fixes are not told apart from network errors yet.
|
||||
**Found:** 2026-10-31, issue #40: the app showed "Connecting… waiting for the Proton Drive
|
||||
service" for days, `pdfs diagnose` only reported the control socket missing, and the reporter
|
||||
uninstalled.
|
||||
@@ -607,9 +634,23 @@ four requests, so a broken account sends tens of thousands a day, which may also
|
||||
Proton's sign-in blocks (#39). The control socket is bound only after the mount succeeds, and
|
||||
the error is written nowhere a front end can read it.
|
||||
|
||||
**Fix.** Not done. Back off exponentially, treat key, `KeysLocked` and 4xx errors as permanent
|
||||
with a long back-off, and write the last error where the app, the tray and `diagnose` can
|
||||
show it (a state file, and `STATUS=` to systemd).
|
||||
**Fix.**
|
||||
- The retry doubles from 5 s to a 2-minute ceiling (`mount_retry_delay` in
|
||||
`crates/pdfs-cli/src/main.rs`), about 720 attempts a day instead of 17,000. The ceiling stays
|
||||
at minutes because a cold start without a network fails the same way and has to mount soon
|
||||
after the network returns.
|
||||
- Each failure is written to `mount-failure.json` in the state directory
|
||||
(`crates/pdfs-core/src/mountfail.rs`), with the error, since when it fails and the attempt
|
||||
count. A mount that comes up removes it, and so does a new daemon.
|
||||
- The app shows "Can't open your Drive" with the error and a Retry button instead of
|
||||
"Connecting…". The tray says "Signed in as … — can't open your Drive". `pdfs diagnose` reports
|
||||
`[FAIL] mount: failing for …`. `systemctl --user status` shows the error as the unit's status.
|
||||
- Not done: key, `KeysLocked` and 4xx errors still retry like network errors, only less often.
|
||||
|
||||
**Test:** `a_failing_mount_backs_off_to_a_ceiling` in `pdfs-cli`;
|
||||
`a_recorded_failure_reads_back_until_cleared` in `pdfs-core`. On 2026-10-31 a second
|
||||
`pdfs daemon` beside the running one failed on the database lock three times in 18 s (at 0, 5
|
||||
and 15 s) and recorded `attempts: 3` with the lock error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: German\n"
|
||||
@@ -424,14 +424,27 @@ msgstr "Nicht verbunden"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "Der Proton-Drive-Dienst läuft nicht."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Proton Drive kann nicht geöffnet werden"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"Der Proton-Drive-Dienst kann wiederholt nicht starten und versucht es "
|
||||
"selbstständig erneut. Letzter Fehler: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Verbindung wird hergestellt …"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "Warten auf den Start des Proton-Drive-Dienstes."
|
||||
|
||||
@@ -2919,7 +2932,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Synchronisierung bis zur Fortsetzung pausiert"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Synchronisierung pausiert"
|
||||
|
||||
@@ -5147,7 +5160,7 @@ msgstr "Wird {time} fortgesetzt"
|
||||
msgid "Until you resume"
|
||||
msgstr "Bis zur manuellen Fortsetzung"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6382,52 +6395,57 @@ msgstr "Proton Drive reagiert nicht"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Angemeldet als {username} – gesperrt"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Angemeldet als {username} – Proton Drive kann nicht geöffnet werden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Angemeldet als {username} – nicht verbunden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Nicht angemeldet"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Fehler: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive hat unerwartet geantwortet"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Sitzung abgelaufen – erneut anmelden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Anmeldung nicht gespeichert – Schlüsselbund entsperren"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Update nicht abgeschlossen – Proton Drive neu starten"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "Aktuell in {mountpoint} ({n} angeheftet)"
|
||||
msgstr[1] "Aktuell in {mountpoint} ({n} angeheftet)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Wird synchronisiert – {queued} ({n} angeheftet)"
|
||||
msgstr[1] "Wird synchronisiert – {queued} ({n} angeheftet)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6435,60 +6453,60 @@ msgstr[0] "Offline – nur zwischengespeicherte Dateien ({n} angeheftet)"
|
||||
msgstr[1] "Offline – nur zwischengespeicherte Dateien ({n} angeheftet)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Offline – {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} Problem – Anzeigen"
|
||||
msgstr[1] "{n} Probleme – Anzeigen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Anmelden …"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Erneut anmelden …"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Proton Drive öffnen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Proton Drive neu starten"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Ordner öffnen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Synchronisierung fortsetzen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Synchronisierung pausieren"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Verbinden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Symbol im Benachrichtigungsfeld ausblenden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Proton Drive beenden …"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Beenden"
|
||||
|
||||
|
||||
+47
-29
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
@@ -420,14 +420,27 @@ msgstr "Not connected"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "The Proton Drive service isn't running."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Can't open your Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Connecting…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "Waiting for the Proton Drive service to start."
|
||||
|
||||
@@ -2880,7 +2893,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Sync paused until you resume"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Sync paused"
|
||||
|
||||
@@ -5046,7 +5059,7 @@ msgstr "Resumes {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Until you resume"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6257,52 +6270,57 @@ msgstr "Proton Drive isn't responding"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Signed in as {username} — locked"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Signed in as {username} — can't open your Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Signed in as {username} — not connected"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Not signed in"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Error: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive sent an unexpected reply"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Session expired — sign in again"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Sign-in not saved — unlock your keyring"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Update not finished — restart Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[1] "Up to date in {mountpoint} ({n} pinned)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[1] "Syncing — {queued} ({n} pinned)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6310,60 +6328,60 @@ msgstr[0] "Offline — cached files only ({n} pinned)"
|
||||
msgstr[1] "Offline — cached files only ({n} pinned)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Offline — {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} issue — View"
|
||||
msgstr[1] "{n} issues — View"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Sign In…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Sign In Again…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Open Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Restart Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Open Folder"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Resume Sync"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Pause Sync"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Connect"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Hide Tray Icon"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Stop Proton Drive…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Quit"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: Spanish\n"
|
||||
@@ -422,14 +422,27 @@ msgstr "Sin conexión"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "El servicio de Proton Drive no está en ejecución."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "No se puede abrir Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"El servicio de Proton Drive no consigue iniciarse y vuelve a intentarlo por "
|
||||
"sí solo. Último error: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Conectando…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "Esperando a que se inicie el servicio de Proton Drive."
|
||||
|
||||
@@ -2902,7 +2915,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Sincronización en pausa hasta que la reanude"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Sincronización en pausa"
|
||||
|
||||
@@ -5106,7 +5119,7 @@ msgstr "Se reanuda {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Hasta que la reanude"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6338,52 +6351,57 @@ msgstr "Proton Drive no responde"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Sesión iniciada como {username} — bloqueado"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Sesión iniciada como {username} — no se puede abrir Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Sesión iniciada como {username} — sin conexión"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Sesión no iniciada"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Error: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive envió una respuesta inesperada"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Sesión caducada: vuelva a iniciar sesión"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Inicio de sesión no guardado: desbloquee el depósito de claves"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Actualización sin terminar: reinicie Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "Actualizado en {mountpoint} ({n} fijado)"
|
||||
msgstr[1] "Actualizado en {mountpoint} ({n} fijados)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Sincronizando: {queued} ({n} anclado)"
|
||||
msgstr[1] "Sincronizando: {queued} ({n} anclados)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6391,60 +6409,60 @@ msgstr[0] "Sin conexión: solo archivos en caché ({n} anclado)"
|
||||
msgstr[1] "Sin conexión: solo archivos en caché ({n} anclados)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Sin conexión: {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} problema: ver"
|
||||
msgstr[1] "{n} problemas: ver"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Iniciar sesión…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Volver a iniciar sesión…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Abrir Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Reiniciar Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Abrir carpeta"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Reanudar la sincronización"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Pausar la sincronización"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Conectar"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Ocultar el icono de la bandeja"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Detener Proton Drive…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Salir"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: French\n"
|
||||
@@ -424,14 +424,27 @@ msgstr "Non connecté"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "Le service Proton Drive n’est pas en cours d’exécution."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Impossible d’ouvrir Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"Le service Proton Drive n’arrive pas à démarrer et réessaie de lui-même. "
|
||||
"Dernière erreur : {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Connexion…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "En attente du démarrage du service Proton Drive."
|
||||
|
||||
@@ -2919,7 +2932,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Synchronisation en pause jusqu’à la reprise"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Synchronisation en pause"
|
||||
|
||||
@@ -5149,7 +5162,7 @@ msgstr "Reprise {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Jusqu’à la reprise"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6386,52 +6399,57 @@ msgstr "Proton Drive ne répond pas"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Connecté en tant que {username} — verrouillé"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Connecté en tant que {username} — impossible d’ouvrir Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Connecté en tant que {username} — non connecté"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Non connecté"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Erreur : {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive a envoyé une réponse inattendue"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Session expirée — reconnectez-vous"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Connexion non enregistrée — déverrouillez votre trousseau"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Mise à jour inachevée — redémarrez Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "À jour dans {mountpoint} ({n} épinglé)"
|
||||
msgstr[1] "À jour dans {mountpoint} ({n} épinglés)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Synchronisation — {queued} ({n} épinglé)"
|
||||
msgstr[1] "Synchronisation — {queued} ({n} épinglés)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6439,60 +6457,60 @@ msgstr[0] "Hors ligne — fichiers en cache uniquement ({n} épinglé)"
|
||||
msgstr[1] "Hors ligne — fichiers en cache uniquement ({n} épinglés)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Hors ligne — {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} problème — Afficher"
|
||||
msgstr[1] "{n} problèmes — Afficher"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Se connecter…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Se reconnecter…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Ouvrir Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Redémarrer Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Ouvrir le dossier"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Reprendre la synchronisation"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Mettre en pause la synchronisation"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Connecter"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Masquer l’icône de la zone de notification"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Arrêter Proton Drive…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Quitter"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: Italian\n"
|
||||
@@ -420,14 +420,27 @@ msgstr "Non connesso"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "Il servizio di Proton Drive non è in esecuzione."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Impossibile aprire Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"Il servizio di Proton Drive non riesce ad avviarsi e riprova da solo. Ultimo "
|
||||
"errore: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Connessione…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "In attesa dell’avvio del servizio di Proton Drive."
|
||||
|
||||
@@ -2917,7 +2930,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Sincronizzazione in pausa finché non riprendi"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Sincronizzazione in pausa"
|
||||
|
||||
@@ -5125,7 +5138,7 @@ msgstr "Riprende {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Finché non riprendi"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6362,52 +6375,57 @@ msgstr "Proton Drive non risponde"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Accesso effettuato come {username} — bloccato"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Accesso effettuato come {username} — impossibile aprire Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Accesso effettuato come {username} — non connesso"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Accesso non eseguito"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Errore: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive ha inviato una risposta inattesa"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Sessione scaduta: accedi di nuovo"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Accesso non salvato: sblocca il portachiavi"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Aggiornamento non completato: riavvia Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "Aggiornato in {mountpoint} ({n} fissato)"
|
||||
msgstr[1] "Aggiornato in {mountpoint} ({n} fissati)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Sincronizzazione: {queued} ({n} fissato)"
|
||||
msgstr[1] "Sincronizzazione: {queued} ({n} fissati)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6415,60 +6433,60 @@ msgstr[0] "Offline: solo file in cache ({n} fissato)"
|
||||
msgstr[1] "Offline: solo file in cache ({n} fissati)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Offline: {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} problema: visualizza"
|
||||
msgstr[1] "{n} problemi: visualizza"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Accedi…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Accedi di nuovo…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Apri Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Riavvia Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Apri cartella"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Riprendi sincronizzazione"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Metti in pausa sincronizzazione"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Connetti"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Nascondi icona nell'area di notifica"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Arresta Proton Drive…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Esci"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: Dutch\n"
|
||||
@@ -421,14 +421,27 @@ msgstr "Niet verbonden"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "De Proton Drive-service draait niet."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Kan Proton Drive niet openen"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"De Proton Drive-service kan steeds niet starten en probeert het zelf "
|
||||
"opnieuw. Laatste fout: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Verbinden…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "Wachten tot de Proton Drive-service is gestart."
|
||||
|
||||
@@ -2907,7 +2920,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Synchronisatie gepauzeerd totdat je hervat"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Synchronisatie gepauzeerd"
|
||||
|
||||
@@ -5119,7 +5132,7 @@ msgstr "Hervat {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Totdat je hervat"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6348,52 +6361,57 @@ msgstr "Proton Drive reageert niet"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Aangemeld als {username} — vergrendeld"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Aangemeld als {username} — kan Proton Drive niet openen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Aangemeld als {username} — niet verbonden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Niet aangemeld"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Fout: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive gaf een onverwacht antwoord"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Sessie verlopen — meld je opnieuw aan"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Aanmelding niet opgeslagen — ontgrendel je sleutelbos"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Update niet voltooid — start Proton Drive opnieuw"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "Bijgewerkt in {mountpoint} ({n} vastgezet)"
|
||||
msgstr[1] "Bijgewerkt in {mountpoint} ({n} vastgezet)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Synchroniseren — {queued} ({n} vastgezet)"
|
||||
msgstr[1] "Synchroniseren — {queued} ({n} vastgezet)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6401,60 +6419,60 @@ msgstr[0] "Offline — alleen bestanden in cache ({n} vastgezet)"
|
||||
msgstr[1] "Offline — alleen bestanden in cache ({n} vastgezet)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Offline — {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} probleem — Bekijken"
|
||||
msgstr[1] "{n} problemen — Bekijken"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Aanmelden…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Opnieuw aanmelden…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Proton Drive openen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Proton Drive opnieuw starten"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Map openen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Synchronisatie hervatten"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Synchronisatie pauzeren"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Verbinden"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Pictogram in systeemvak verbergen"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Proton Drive stoppen…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Afsluiten"
|
||||
|
||||
|
||||
+45
-29
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 3.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -397,14 +397,25 @@ msgstr ""
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr ""
|
||||
|
||||
@@ -2799,7 +2810,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr ""
|
||||
|
||||
@@ -4894,7 +4905,7 @@ msgstr ""
|
||||
msgid "Until you resume"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6082,52 +6093,57 @@ msgstr ""
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6135,60 +6151,60 @@ msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr ""
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: Polish\n"
|
||||
@@ -425,14 +425,27 @@ msgstr "Brak połączenia"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "Usługa Proton Drive nie jest uruchomiona."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Nie można otworzyć Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"Usługa Proton Drive nie może się uruchomić i sama ponawia próby. Ostatni "
|
||||
"błąd: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Łączenie…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "Oczekiwanie na uruchomienie usługi Proton Drive."
|
||||
|
||||
@@ -2973,7 +2986,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Wstrzymano synchronizację do czasu wznowienia"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Wstrzymano synchronizację"
|
||||
|
||||
@@ -5200,7 +5213,7 @@ msgstr "Wznowienie {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Do czasu wznowienia"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6451,38 +6464,43 @@ msgstr "Proton Drive nie odpowiada"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Zalogowano jako {username} — zablokowane"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Zalogowano jako {username} — nie można otworzyć Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Zalogowano jako {username} — brak połączenia"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Nie zalogowano"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Błąd: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "Proton Drive wysłał nieoczekiwaną odpowiedź"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Sesja wygasła — zaloguj się ponownie"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Logowanie nie zostało zapisane — odblokuj bazę kluczy"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Aktualizacja niedokończona — uruchom ponownie Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
@@ -6490,7 +6508,7 @@ msgstr[0] "Aktualne w {mountpoint} ({n} przypięty)"
|
||||
msgstr[1] "Aktualne w {mountpoint} ({n} przypięte)"
|
||||
msgstr[2] "Aktualne w {mountpoint} ({n} przypiętych)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
@@ -6498,7 +6516,7 @@ msgstr[0] "Synchronizowanie — {queued} (przypięte: {n})"
|
||||
msgstr[1] "Synchronizowanie — {queued} (przypięte: {n})"
|
||||
msgstr[2] "Synchronizowanie — {queued} (przypięte: {n})"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6507,13 +6525,13 @@ msgstr[1] "Offline — tylko pliki w pamięci podręcznej (przypięte: {n})"
|
||||
msgstr[2] "Offline — tylko pliki w pamięci podręcznej (przypięte: {n})"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Offline — {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
@@ -6521,47 +6539,47 @@ msgstr[0] "{n} problem — wyświetl"
|
||||
msgstr[1] "{n} problemy — wyświetl"
|
||||
msgstr[2] "{n} problemów — wyświetl"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Zaloguj się…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Zaloguj się ponownie…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Otwórz Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Uruchom ponownie Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Otwórz katalog"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Wznów synchronizację"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Wstrzymaj synchronizację"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Połącz"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Ukryj ikonę w obszarze powiadamiania"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Zatrzymaj Proton Drive…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Zakończ"
|
||||
|
||||
|
||||
+47
-29
@@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: proton-drive-linux 2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
|
||||
"POT-Creation-Date: 2026-10-31 10:26+0100\n"
|
||||
"POT-Creation-Date: 2026-10-31 19:26+0100\n"
|
||||
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
|
||||
"Last-Translator: Proton Drive for Linux contributors\n"
|
||||
"Language-Team: Portuguese (Brazil)\n"
|
||||
@@ -420,14 +420,27 @@ msgstr "Não conectado"
|
||||
msgid "The Proton Drive service isn't running."
|
||||
msgstr "O serviço do Proton Drive não está em execução."
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1803
|
||||
#: crates/pdfs-gui/src/app/main.rs:1813
|
||||
msgid "Can't open your Drive"
|
||||
msgstr "Não foi possível abrir o Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1816
|
||||
#, rust-format
|
||||
msgid ""
|
||||
"The Proton Drive service keeps failing to start and tries again on its own. "
|
||||
"The last error was: {error}"
|
||||
msgstr ""
|
||||
"O serviço do Proton Drive não consegue iniciar e tenta de novo sozinho. "
|
||||
"Último erro: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1824
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:355
|
||||
#: crates/pdfs-gui/src/hud/mod.rs:269 crates/pdfs-gui/src/hud/mod.rs:579
|
||||
#: crates/pdfs-gui/src/main.rs:475 crates/pdfs-gui/src/main.rs:517
|
||||
#: crates/pdfs-gui/src/main.rs:493 crates/pdfs-gui/src/main.rs:535
|
||||
msgid "Connecting…"
|
||||
msgstr "Conectando…"
|
||||
|
||||
#: crates/pdfs-gui/src/app/main.rs:1804
|
||||
#: crates/pdfs-gui/src/app/main.rs:1825
|
||||
msgid "Waiting for the Proton Drive service to start."
|
||||
msgstr "Aguardando o serviço do Proton Drive iniciar."
|
||||
|
||||
@@ -2903,7 +2916,7 @@ msgid "Sync paused until you resume"
|
||||
msgstr "Sincronização pausada até você retomar"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/locations.rs:570
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:290
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1445 crates/pdfs-gui/src/main.rs:308
|
||||
msgid "Sync paused"
|
||||
msgstr "Sincronização pausada"
|
||||
|
||||
@@ -5104,7 +5117,7 @@ msgstr "Retoma {time}"
|
||||
msgid "Until you resume"
|
||||
msgstr "Até você retomar"
|
||||
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:292
|
||||
#: crates/pdfs-gui/src/app/pages/status.rs:1464 crates/pdfs-gui/src/main.rs:310
|
||||
#, rust-format
|
||||
msgid "{n} change needs attention"
|
||||
msgid_plural "{n} changes need attention"
|
||||
@@ -6335,52 +6348,57 @@ msgstr "O Proton Drive não está respondendo"
|
||||
msgid "Signed in as {username} — locked"
|
||||
msgstr "Conectado como {username} — bloqueado"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:200
|
||||
#: crates/pdfs-gui/src/main.rs:209
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — can't open your Drive"
|
||||
msgstr "Conectado como {username} — não foi possível abrir o Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:218
|
||||
#, rust-format
|
||||
msgid "Signed in as {username} — not connected"
|
||||
msgstr "Conectado como {username} — sem conexão"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:205
|
||||
#: crates/pdfs-gui/src/main.rs:223
|
||||
msgid "Not signed in"
|
||||
msgstr "Não conectado"
|
||||
|
||||
#. Translators: {error} is an error message, in English.
|
||||
#: crates/pdfs-gui/src/main.rs:208
|
||||
#: crates/pdfs-gui/src/main.rs:226
|
||||
#, rust-format
|
||||
msgid "Error: {error}"
|
||||
msgstr "Erro: {error}"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:230
|
||||
#: crates/pdfs-gui/src/main.rs:248
|
||||
msgid "Proton Drive sent an unexpected reply"
|
||||
msgstr "O Proton Drive enviou uma resposta inesperada"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:284
|
||||
#: crates/pdfs-gui/src/main.rs:302
|
||||
msgid "Session expired — sign in again"
|
||||
msgstr "Sessão expirada — entre novamente"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:286
|
||||
#: crates/pdfs-gui/src/main.rs:304
|
||||
msgid "Sign-in not saved — unlock your keyring"
|
||||
msgstr "Login não salvo — desbloqueie o chaveiro"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:289
|
||||
#: crates/pdfs-gui/src/main.rs:307
|
||||
msgid "Update not finished — restart Proton Drive"
|
||||
msgstr "Atualização não concluída — reinicie o Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:299
|
||||
#: crates/pdfs-gui/src/main.rs:317
|
||||
#, rust-format
|
||||
msgid "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgid_plural "Up to date in {mountpoint} ({n} pinned)"
|
||||
msgstr[0] "Atualizado em {mountpoint} ({n} fixado)"
|
||||
msgstr[1] "Atualizado em {mountpoint} ({n} fixados)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:306
|
||||
#: crates/pdfs-gui/src/main.rs:324
|
||||
#, rust-format
|
||||
msgid "Syncing — {queued} ({n} pinned)"
|
||||
msgid_plural "Syncing — {queued} ({n} pinned)"
|
||||
msgstr[0] "Sincronizando — {queued} ({n} fixado)"
|
||||
msgstr[1] "Sincronizando — {queued} ({n} fixados)"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:313
|
||||
#: crates/pdfs-gui/src/main.rs:331
|
||||
#, rust-format
|
||||
msgid "Offline — cached files only ({n} pinned)"
|
||||
msgid_plural "Offline — cached files only ({n} pinned)"
|
||||
@@ -6388,60 +6406,60 @@ msgstr[0] "Offline — somente arquivos em cache ({n} fixado)"
|
||||
msgstr[1] "Offline — somente arquivos em cache ({n} fixados)"
|
||||
|
||||
#. Translators: {queued} is a queue summary such as "3 uploads queued".
|
||||
#: crates/pdfs-gui/src/main.rs:319
|
||||
#: crates/pdfs-gui/src/main.rs:337
|
||||
#, rust-format
|
||||
msgid "Offline — {queued}"
|
||||
msgstr "Offline — {queued}"
|
||||
|
||||
#. Translators: a menu item that opens the list of failing changes.
|
||||
#: crates/pdfs-gui/src/main.rs:358
|
||||
#: crates/pdfs-gui/src/main.rs:376
|
||||
#, rust-format
|
||||
msgid "{n} issue — View"
|
||||
msgid_plural "{n} issues — View"
|
||||
msgstr[0] "{n} problema — Ver"
|
||||
msgstr[1] "{n} problemas — Ver"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:465
|
||||
#: crates/pdfs-gui/src/main.rs:483
|
||||
msgid "Sign In…"
|
||||
msgstr "Entrar…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:466
|
||||
#: crates/pdfs-gui/src/main.rs:484
|
||||
msgid "Sign In Again…"
|
||||
msgstr "Entrar novamente…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:469
|
||||
#: crates/pdfs-gui/src/main.rs:487
|
||||
msgid "Open Proton Drive"
|
||||
msgstr "Abrir o Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:473
|
||||
#: crates/pdfs-gui/src/main.rs:491
|
||||
msgid "Restart Proton Drive"
|
||||
msgstr "Reiniciar o Proton Drive"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:482
|
||||
#: crates/pdfs-gui/src/main.rs:500
|
||||
msgid "Open Folder"
|
||||
msgstr "Abrir pasta"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:494
|
||||
#: crates/pdfs-gui/src/main.rs:512
|
||||
msgid "Resume Sync"
|
||||
msgstr "Retomar sincronização"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:496
|
||||
#: crates/pdfs-gui/src/main.rs:514
|
||||
msgid "Pause Sync"
|
||||
msgstr "Pausar sincronização"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:515
|
||||
#: crates/pdfs-gui/src/main.rs:533
|
||||
msgid "Connect"
|
||||
msgstr "Conectar"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:522
|
||||
#: crates/pdfs-gui/src/main.rs:540
|
||||
msgid "Hide Tray Icon"
|
||||
msgstr "Ocultar ícone da bandeja"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:526
|
||||
#: crates/pdfs-gui/src/main.rs:544
|
||||
msgid "Stop Proton Drive…"
|
||||
msgstr "Parar o Proton Drive…"
|
||||
|
||||
#: crates/pdfs-gui/src/main.rs:530
|
||||
#: crates/pdfs-gui/src/main.rs:548
|
||||
msgid "Quit"
|
||||
msgstr "Sair"
|
||||
|
||||
|
||||
Reference in new issue
Block a user