feat(core,fuse,gui): find photos that look alike

The duplicate finder gains an Exact / Similar switch. Similar asks the
daemon for PhotoSimilar, which starts a background pass that hashes the
thumbnail of every shot not hashed yet and replies at once with the sets
found so far and how far hashing has got. The dialog shows a progress bar
and asks again until the pass is done, then lists the sets.

The pass takes thumbnails from the server only: a photo without one is
skipped for the run instead of downloaded whole, and the dialog says how
many photos could not be compared. Decoding and hashing are spread over
the machine's cores.

Every similar photo starts ticked Keep, since photos that look alike can
be different shots. A similar photo stands for its whole shot, so one not
kept goes to Trash with the rest of its shot.
This commit is contained in:
nvrl committed 2026-09-28 09:16:54 +02:00
1 parent c98ed0d83b
commit a90eeecda9
16 files changed
+1264 -238

No files matched your search

+19 -1
View File
@@ -91,6 +91,12 @@ pub enum Request {
/// Sets of photos that are byte-identical copies of each other, going by
/// the server's content hash. Replies with [`Response::PhotoDuplicates`].
PhotoDuplicates,
/// Sets of shots that look alike going by their thumbnails: resized or
/// re-encoded copies, and bursts of one moment. Starts hashing the
/// thumbnails not hashed yet in the background and replies at once with
/// the sets found so far; a front-end asks again until the reply's
/// `hashing` is `None`. Replies with [`Response::PhotoSimilar`].
PhotoSimilar,
/// The towns the photos were taken in, the one with the most photos first.
/// Only photos whose uploader recorded a location count. Replies with
/// [`Response::Places`].
@@ -1481,7 +1487,8 @@ pub struct PhotoLocation {
}
/// How many of the timeline's photos a running refresh has read the metadata
/// (and so the location) of, out of how many. Part of [`Response::Places`].
/// (and so the location) of, out of how many. Part of [`Response::Places`],
/// and of [`Response::PhotoSimilar`] for how many shots have been hashed.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct MappingProgress {
pub done: usize,
@@ -1849,6 +1856,17 @@ pub enum Response {
/// The duplicate sets (reply to [`Request::PhotoDuplicates`]), newest
/// first. Each set lists the copy worth keeping first.
PhotoDuplicates { sets: Vec<Vec<PhotoItem>> },
/// The sets of alike shots (reply to [`Request::PhotoSimilar`]), newest
/// first, each in capture order; and how far hashing has got, `None`
/// once no pass is running. `uncompared` counts the shots left out
/// because no thumbnail could be read for them.
PhotoSimilar {
sets: Vec<Vec<PhotoItem>>,
#[serde(default)]
hashing: Option<MappingProgress>,
#[serde(default)]
uncompared: usize,
},
/// The towns the photos were taken in (reply to [`Request::PhotoPlaces`]),
/// and how far a running timeline refresh has got reading photo locations
/// — `None` when none is reading any.
+8
View File
@@ -465,6 +465,14 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
Ok(sets) => CtlResponse::PhotoDuplicates { sets },
Err(e) => CtlResponse::error(e),
},
Ok(CtlRequest::PhotoSimilar) => match core.photo_similar() {
Ok((sets, hashing, uncompared)) => CtlResponse::PhotoSimilar {
sets,
hashing,
uncompared,
},
Err(e) => CtlResponse::error(e),
},
Ok(CtlRequest::PhotoPlaces) => match core.photo_places() {
Ok(items) => CtlResponse::Places {
items,
+3
View File
@@ -557,6 +557,9 @@ struct Core {
/// The same, for the album listing.
albums_refreshing: Arc<AtomicBool>,
trash_refreshing: Arc<AtomicBool>,
/// True while the similar-photo pass hashes thumbnails (see
/// [`Core::photo_similar`]), so asking again doesn't start a second one.
similar_hashing: Arc<AtomicBool>,
/// Fires whenever a trash refresh persists a batch or finishes, so a
/// `ListTrash` request waiting on a first-ever refresh wakes on progress
/// instead of polling or blocking for the whole run. See
+1
View File
@@ -392,6 +392,7 @@ pub fn mount(
upload_cancel: Arc::new(Mutex::new(HashMap::new())),
timeline_refreshing: Arc::new(AtomicBool::new(false)),
timeline_progress: Arc::new(Mutex::new(None)),
similar_hashing: Arc::new(AtomicBool::new(false)),
albums_refreshing: Arc::new(AtomicBool::new(false)),
trash_refreshing: Arc::new(AtomicBool::new(false)),
trash_progress: Arc::new(tokio::sync::Notify::new()),
+169 -2
View File
@@ -43,6 +43,8 @@ const THUMB_QUALITY: u8 = 82;
/// thumbnails. Bounded: a screenful of 20 MB digicam JPEGs would otherwise
/// saturate the link and starve the rest of the daemon.
pub(crate) const THUMB_GEN_CONCURRENCY: usize = 4;
/// Shots the similar-photo pass fetches thumbnails for at a time.
const SIMILAR_CHUNK: usize = 64;
/// Images processed between recursive-build progress updates.
const THUMB_BUILD_CHUNK: usize = 16;
/// A recursive build may wait briefly for an opportunistic tile job that
@@ -81,6 +83,46 @@ fn ratio_of(bytes: &[u8]) -> Option<f64> {
(height > 0).then(|| f64::from(width) / f64::from(height))
}
/// The difference hash of each cached thumbnail in `paths`, keyed like the
/// input, or [`pdfs_core::similar::NO_HASH`] for one that can't be decoded or
/// shows no picture. Decoding is spread over the machine's cores.
fn similar_hashes(paths: Vec<(String, PathBuf)>) -> Vec<(String, u64)> {
fn hash(path: &Path) -> u64 {
image::ImageReader::open(path)
.ok()
.and_then(|reader| reader.with_guessed_format().ok())
.and_then(|reader| reader.decode().ok())
.and_then(|image| {
let luma = image.to_luma8();
pdfs_core::similar::difference_hash(
luma.width() as usize,
luma.height() as usize,
luma.as_raw(),
)
})
.unwrap_or(pdfs_core::similar::NO_HASH)
}
let threads = std::thread::available_parallelism().map_or(1, |n| n.get());
let per_thread = paths.len().div_ceil(threads).max(1);
std::thread::scope(|scope| {
let workers: Vec<_> = paths
.chunks(per_thread)
.map(|chunk| {
scope.spawn(move || {
chunk
.iter()
.map(|(uid, path)| (uid.clone(), hash(path)))
.collect::<Vec<_>>()
})
})
.collect();
workers
.into_iter()
.flat_map(|worker| worker.join().unwrap_or_default())
.collect()
})
}
/// A thumbnail the daemon made itself, and the aspect ratio of the photo it was
/// made from (free at that point — the full image had to be decoded anyway).
struct GeneratedThumb {
@@ -751,6 +793,83 @@ impl Core {
.collect())
}
/// The sets of shots that look alike, as wire items (see
/// [`db::Db::photo_similar`]), how far hashing has got while it runs, and
/// how many shots couldn't be compared once it's done. Starts a hashing
/// pass for the shots not hashed yet unless one is running.
pub(crate) fn photo_similar(
&self,
) -> CoreResult<(Vec<Vec<PhotoItem>>, Option<MappingProgress>, usize)> {
self.spawn_similar_hashing();
let sets = self.db.photo_similar().map_err(CoreError::from)?;
let (done, total) = self.db.similar_progress().map_err(CoreError::from)?;
let hashing = self
.similar_hashing
.load(Ordering::SeqCst)
.then_some(MappingProgress { done, total });
let uncompared = if hashing.is_some() { 0 } else { total - done };
let sets = sets
.into_iter()
.map(|set| set.into_iter().map(|p| self.photo_item(p)).collect())
.collect();
Ok((sets, hashing, uncompared))
}
fn spawn_similar_hashing(&self) {
if self.similar_hashing.swap(true, Ordering::SeqCst) {
return;
}
let core = self.clone();
let spawned = std::thread::Builder::new()
.name("pdfs-similar".into())
.spawn(move || {
if let Err(e) = core.hash_similar() {
warn!(error = %e, "similar-photo hashing failed");
}
core.similar_hashing.store(false, Ordering::SeqCst);
});
if let Err(e) = spawned {
warn!(error = %e, "could not start similar-photo hashing");
self.similar_hashing.store(false, Ordering::SeqCst);
}
}
/// Hash the look of every shot not hashed yet, a chunk at a time. Only
/// thumbnails the server has are used: a photo without one is skipped
/// rather than downloaded whole, and tried again on the next pass.
fn hash_similar(&self) -> CoreResult<()> {
let mut tried: HashSet<String> = HashSet::new();
loop {
if self.shutdown.is_stopping() {
return Ok(());
}
// Shots tried this pass without a thumbnail are still unhashed and
// come back first, so ask past them.
let chunk: Vec<NodeUid> = self
.db
.photos_unhashed(tried.len() + SIMILAR_CHUNK)
.map_err(CoreError::from)?
.into_iter()
.filter(|photo| tried.insert(photo.uid.clone()))
.take(SIMILAR_CHUNK)
.filter_map(|photo| parse_uid(&photo.uid))
.collect();
if chunk.is_empty() {
return Ok(());
}
let paths: Vec<(String, PathBuf)> = self
.photo_thumbs_with(&chunk, false)
.into_iter()
.filter_map(|thumb| Some((thumb.uid, PathBuf::from(thumb.path?))))
.collect();
for (uid, hash) in similar_hashes(paths) {
self.db
.photo_set_similar_hash(&uid, hash)
.map_err(CoreError::from)?;
}
}
}
/// The towns the photos were taken in, as wire items. A town the bundled
/// table no longer knows (the stored id came from an older table) is left
/// out rather than shown without a name.
@@ -961,6 +1080,13 @@ impl Core {
/// milliseconds where the raw costs a full download and a raw decode. The
/// tile still answers under the uid that was asked for.
pub(crate) fn photo_thumbs(&self, requested: &[NodeUid]) -> Vec<PhotoThumb> {
self.photo_thumbs_with(requested, true)
}
/// [`Core::photo_thumbs`], with local generation only when `generate` is
/// set. Without it, a photo the server has no thumbnail for is left
/// without one rather than downloaded whole.
fn photo_thumbs_with(&self, requested: &[NodeUid], generate: bool) -> Vec<PhotoThumb> {
let ttype = ThumbnailType::Thumbnail.as_i32();
// Only a raw can be stood in for, and that is one batched read; the
// per-shot group lookup is then paid for raws alone, not for a page of
@@ -1061,7 +1187,7 @@ impl Core {
// that never generated one. Off the request path: a full-size photo
// takes far longer to fetch than the whole rest of the batch, and the
// thumbnails that *are* ready must not wait behind it.
if !missing.is_empty() {
if generate && !missing.is_empty() {
self.spawn_generate_thumbs(missing, &tags, &names, ThumbJob::Photos);
}
}
@@ -1964,10 +2090,11 @@ mod thumb_tests {
use super::{
RawTempFile, ScaleAttempt, THUMB_EDGE, UPLOAD_THUMB_MAX_BYTES, exiftool_binary,
fit_upload_thumb, is_video_name, ratio_of, scale_thumbnail, scale_thumbnail_with_exiftool,
thumbnail_build_may_start, upload_thumbnails_with, video_thumbnail,
similar_hashes, thumbnail_build_may_start, upload_thumbnails_with, video_thumbnail,
};
use pdfs_core::control::{ThumbnailBuildStatus, is_thumbnail_image_name};
use proton_drive_rs::ThumbnailType;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::os::unix::fs::PermissionsExt as _;
use std::time::Duration;
@@ -2241,6 +2368,46 @@ mod thumb_tests {
assert!(thumbnail_build_may_start(&idle, "pictures/second").unwrap());
}
#[test]
fn thumbnails_hash_alike_across_sizes_and_undecodable_ones_get_no_hash() {
let tmp = scratch("similar-hashes");
std::fs::create_dir_all(&tmp).unwrap();
// One picture at two sizes: a diagonal gradient with a bright corner.
let picture = |width: u32, height: u32| {
let image = image::GrayImage::from_fn(width, height, |x, y| {
let (fx, fy) = (x * 4 / width, y * 4 / height);
image::Luma([if fx + fy == 0 {
250
} else {
(fx * 40 + fy * 20) as u8
}])
});
let mut bytes = Vec::new();
image::codecs::jpeg::JpegEncoder::new(&mut bytes)
.encode_image(&image)
.unwrap();
bytes
};
std::fs::write(tmp.join("big"), picture(512, 384)).unwrap();
std::fs::write(tmp.join("small"), picture(256, 192)).unwrap();
std::fs::write(tmp.join("torn"), b"not a picture").unwrap();
let hashes: HashMap<String, u64> = similar_hashes(
["big", "small", "torn", "gone"]
.iter()
.map(|name| (name.to_string(), tmp.join(name)))
.collect(),
)
.into_iter()
.collect();
let _ = std::fs::remove_dir_all(&tmp);
assert_eq!(hashes.len(), 4);
assert_ne!(hashes["big"], pdfs_core::similar::NO_HASH);
assert!(pdfs_core::similar::alike(hashes["big"], hashes["small"]));
assert_eq!(hashes["torn"], pdfs_core::similar::NO_HASH);
assert_eq!(hashes["gone"], pdfs_core::similar::NO_HASH);
}
#[test]
fn an_uploaded_photo_carries_its_thumbnail() {
let tmp = scratch("upload-photo");
+265 -35
View File
@@ -6,14 +6,58 @@ const COPY_SIZE: i32 = 150;
/// Each set's copies with their Keep toggles.
type SetToggles = Vec<Vec<(PhotoItem, gtk4::ToggleButton)>>;
/// How often the dialog asks again while the daemon hashes thumbnails.
const HASHING_POLL: std::time::Duration = std::time::Duration::from_secs(1);
/// What the finder looks for.
#[derive(Clone, Copy, PartialEq, Eq)]
enum Finder {
/// Byte-identical copies, going by the server's content hash.
Exact,
/// Shots that look alike, going by their thumbnails.
Similar,
}
/// The dialog's parts that a search fills in.
struct FinderView {
ui: Rc<Ui>,
status: adw::StatusPage,
progress: gtk4::ProgressBar,
note: gtk4::Label,
stack: gtk4::Stack,
trash: gtk4::Button,
toolbar: adw::ToolbarView,
/// Bumped by every new search and when the dialog closes, so a reply or a
/// poll for an older one is dropped.
generation: Cell<u32>,
/// The Trash button's handler for the review on show.
clicked: RefCell<Option<glib::SignalHandlerId>>,
}
/// Open the duplicate finder: every set of photos stored more than once, side
/// by side, with the copy worth keeping ticked. The copies left unticked go to
/// Trash — one file each, never the rest of their shot.
/// Trash — one file each, never the rest of their shot. The Similar switch
/// looks for shots that look alike instead.
pub(crate) fn show_duplicates(ui: &Rc<Ui>) {
let progress = gtk4::ProgressBar::builder()
.show_text(true)
.width_request(280)
.halign(gtk4::Align::Center)
.visible(false)
.build();
let note = gtk4::Label::builder()
.wrap(true)
.justify(gtk4::Justification::Center)
.visible(false)
.build();
note.add_css_class("dim-label");
let status_extra = gtk4::Box::new(gtk4::Orientation::Vertical, 12);
status_extra.append(&progress);
status_extra.append(&note);
let status = adw::StatusPage::builder()
.icon_name("edit-copy-symbolic")
.title(gettext("Looking for duplicates…"))
.vexpand(true)
.child(&status_extra)
.build();
status.add_css_class("compact");
@@ -21,7 +65,6 @@ pub(crate) fn show_duplicates(ui: &Rc<Ui>) {
stack.add_named(&status, Some("status"));
let trash = gtk4::Button::builder()
.label(gettext("Move to Trash"))
.halign(gtk4::Align::End)
.sensitive(false)
.build();
@@ -32,8 +75,25 @@ pub(crate) fn show_duplicates(ui: &Rc<Ui>) {
bottom.set_halign(gtk4::Align::End);
bottom.append(&trash);
let exact = gtk4::ToggleButton::builder()
.label(pgettext("duplicates", "Exact"))
.tooltip_text(gettext("Photos stored more than once"))
.active(true)
.build();
let similar = gtk4::ToggleButton::builder()
.label(pgettext("duplicates", "Similar"))
.tooltip_text(gettext("Photos that look alike"))
.group(&exact)
.build();
let switch = gtk4::Box::new(gtk4::Orientation::Horizontal, 0);
switch.add_css_class("linked");
switch.append(&exact);
switch.append(&similar);
let header = adw::HeaderBar::new();
header.set_title_widget(Some(&switch));
let toolbar = adw::ToolbarView::new();
toolbar.add_top_bar(&adw::HeaderBar::new());
toolbar.add_top_bar(&header);
toolbar.set_content(Some(&stack));
toolbar.add_bottom_bar(&bottom);
toolbar.set_reveal_bottom_bars(false);
@@ -46,47 +106,189 @@ pub(crate) fn show_duplicates(ui: &Rc<Ui>) {
.build();
dialog.present(ui_window(ui).as_ref());
let rx = spawn_request(ui.dirs.control_socket(), Request::PhotoDuplicates);
let ui = ui.clone();
let view = Rc::new(FinderView {
ui: ui.clone(),
status,
progress,
note,
stack,
trash,
toolbar,
generation: Cell::new(0),
clicked: RefCell::new(None),
});
let view_closed = view.clone();
dialog.connect_closed(move |_| {
view_closed
.generation
.set(view_closed.generation.get().wrapping_add(1));
});
let view_switch = view.clone();
similar.connect_toggled(move |similar| {
let finder = if similar.is_active() {
Finder::Similar
} else {
Finder::Exact
};
search(&view_switch, finder);
});
search(&view, Finder::Exact);
}
/// Drop what the dialog shows and look again, for `finder`.
fn search(view: &Rc<FinderView>, finder: Finder) {
let generation = view.generation.get().wrapping_add(1);
view.generation.set(generation);
if let Some(review) = view.stack.child_by_name("review") {
view.stack.remove(&review);
}
if let Some(handler) = view.clicked.take() {
view.trash.disconnect(handler);
}
view.toolbar.set_reveal_bottom_bars(false);
view.status.set_icon_name(Some("edit-copy-symbolic"));
view.status.set_title(&match finder {
Finder::Exact => gettext("Looking for duplicates…"),
Finder::Similar => gettext("Looking for similar photos…"),
});
view.status.set_description(None);
view.progress.set_visible(false);
view.note.set_visible(false);
view.stack.set_visible_child_name("status");
ask(view, finder, generation);
}
/// Ask the daemon for `finder`'s sets and show them, or, while it is still
/// hashing thumbnails, how far it has got — and ask again in a moment.
fn ask(view: &Rc<FinderView>, finder: Finder, generation: u32) {
let request = match finder {
Finder::Exact => Request::PhotoDuplicates,
Finder::Similar => Request::PhotoSimilar,
};
let rx = spawn_request(view.ui.dirs.control_socket(), request);
let view = view.clone();
glib::spawn_future_local(async move {
let sets = match rx.recv().await {
Ok(Ok(Response::PhotoDuplicates { sets })) => sets,
let reply = rx.recv().await;
if view.generation.get() != generation {
return;
}
let failed = match finder {
Finder::Exact => gettext("Couldn't look for duplicates"),
Finder::Similar => gettext("Couldn't look for similar photos"),
};
let (sets, uncompared) = match reply {
Ok(Ok(Response::PhotoDuplicates { sets })) => (sets, 0),
Ok(Ok(Response::PhotoSimilar {
hashing: Some(MappingProgress { done, total }),
..
})) => {
view.status.set_title(&gettext("Comparing photos…"));
view.status.set_description(Some(&gettext(
"Photos are compared by their thumbnails. The first time takes a while.",
)));
view.progress.set_fraction(if total == 0 {
0.0
} else {
done as f64 / total as f64
});
view.progress.set_text(Some(&gettext_f(
// Translators: {done} and {total} are numbers of photos.
"{done} of {total}",
&[("done", &thousands(done)), ("total", &thousands(total))],
)));
view.progress.set_visible(true);
glib::timeout_add_local_once(HASHING_POLL, move || {
if view.generation.get() == generation {
ask(&view, finder, generation);
}
});
return;
}
Ok(Ok(Response::PhotoSimilar {
sets,
hashing: None,
uncompared,
})) => (sets, uncompared),
Ok(Ok(Response::Error { message, .. })) => {
status.set_icon_name(Some("dialog-warning-symbolic"));
status.set_title(&gettext("Couldn't look for duplicates"));
status.set_description(Some(&message));
view.status.set_icon_name(Some("dialog-warning-symbolic"));
view.status.set_title(&failed);
view.status.set_description(Some(&message));
view.progress.set_visible(false);
return;
}
_ => {
status.set_icon_name(Some("dialog-warning-symbolic"));
status.set_title(&gettext("Couldn't look for duplicates"));
status.set_description(Some(&gettext("The mount service didn't respond.")));
view.status.set_icon_name(Some("dialog-warning-symbolic"));
view.status.set_title(&failed);
view.status
.set_description(Some(&gettext("The mount service didn't respond.")));
view.progress.set_visible(false);
return;
}
};
view.progress.set_visible(false);
if sets.is_empty() {
status.set_icon_name(Some("emblem-ok-symbolic"));
status.set_title(&gettext("No duplicates"));
status.set_description(Some(&gettext(
view.status.set_icon_name(Some("emblem-ok-symbolic"));
match finder {
Finder::Exact => {
view.status.set_title(&gettext("No duplicates"));
view.status.set_description(Some(&gettext(
"Every photo in your library is stored once.",
)));
}
Finder::Similar => {
view.status.set_title(&gettext("No similar photos"));
view.status.set_description(Some(&gettext(
"No two photos in your library look alike.",
)));
}
}
if uncompared > 0 {
view.note.set_label(&uncompared_note(uncompared));
view.note.set_visible(true);
}
return;
}
let review = duplicate_review(&ui, sets, &trash);
stack.add_named(&review, Some("review"));
stack.set_visible_child_name("review");
toolbar.set_reveal_bottom_bars(true);
schedule_thumbs(&ui);
let (review, handler) = duplicate_review(&view.ui, sets, &view.trash, finder, uncompared);
view.clicked.replace(Some(handler));
view.stack.add_named(&review, Some("review"));
view.stack.set_visible_child_name("review");
view.toolbar.set_reveal_bottom_bars(true);
schedule_thumbs(&view.ui);
});
}
/// The review list: one section per set, each copy a thumbnail with a Keep
/// toggle. Wires `trash` to move every copy not kept to Trash.
fn duplicate_review(ui: &Rc<Ui>, sets: Vec<Vec<PhotoItem>>, trash: &gtk4::Button) -> gtk4::Widget {
/// Says how many photos the similar finder left out for want of a thumbnail.
fn uncompared_note(n: usize) -> String {
ngettext_f(
"{n} photo couldn't be compared because it has no thumbnail yet.",
"{n} photos couldn't be compared because they have no thumbnail yet.",
n as u64,
&[],
)
}
/// The review list: one section per set, each photo a thumbnail with a Keep
/// toggle. Wires `trash` to move every photo not kept to Trash, and returns
/// that handler so a new search can take it off again.
///
/// Exact copies start with only the first of each set kept. Similar photos
/// all start kept: photos that look alike can still be different shots.
fn duplicate_review(
ui: &Rc<Ui>,
sets: Vec<Vec<PhotoItem>>,
trash: &gtk4::Button,
finder: Finder,
uncompared: usize,
) -> (gtk4::Widget, glib::SignalHandlerId) {
let intro = gtk4::Label::builder()
.label(gettext(
.label(match finder {
Finder::Exact => gettext(
"These photos are stored more than once. The copies you don't keep move to Trash, where you can restore them.",
))
),
Finder::Similar => gettext(
"These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them.",
),
})
.wrap(true)
.xalign(0.0)
.build();
@@ -98,6 +300,15 @@ fn duplicate_review(ui: &Rc<Ui>, sets: Vec<Vec<PhotoItem>>, trash: &gtk4::Button
content.set_margin_start(18);
content.set_margin_end(18);
content.append(&intro);
if uncompared > 0 {
let note = gtk4::Label::builder()
.label(uncompared_note(uncompared))
.wrap(true)
.xalign(0.0)
.build();
note.add_css_class("dim-label");
content.append(&note);
}
// Every copy's toggle, by set, so the button can count what goes.
let toggles: Rc<RefCell<SetToggles>> = Rc::new(RefCell::new(Vec::new()));
@@ -106,19 +317,28 @@ fn duplicate_review(ui: &Rc<Ui>, sets: Vec<Vec<PhotoItem>>, trash: &gtk4::Button
continue;
};
let date = section_heading(first.capture_time, Grouping::Day);
let title = match finder {
// Translators: heading of one set in the duplicates dialog; {date} is the day the photo was taken.
let title = ngettext_f(
Finder::Exact => ngettext_f(
"{date}, {n} copy",
"{date}, {n} copies",
set.len() as u64,
&[("date", &date)],
);
),
// Translators: heading of one set of photos that look alike; {date} is the day the first was taken.
Finder::Similar => ngettext_f(
"{date}, {n} photo",
"{date}, {n} photos",
set.len() as u64,
&[("date", &date)],
),
};
let heading = gtk4::Label::builder().label(title).xalign(0.0).build();
heading.add_css_class("heading");
let row = gtk4::Box::new(gtk4::Orientation::Horizontal, 12);
let mut set_toggles = Vec::new();
for (index, photo) in set.into_iter().enumerate() {
let (card, keep) = copy_card(ui, &photo, index == 0);
let (card, keep) = copy_card(ui, &photo, index == 0 || finder == Finder::Similar);
row.append(&card);
set_toggles.push((photo, keep));
}
@@ -139,12 +359,20 @@ fn duplicate_review(ui: &Rc<Ui>, sets: Vec<Vec<PhotoItem>>, trash: &gtk4::Button
let trash = trash.clone();
move || {
let n = doomed(&toggles.borrow()).len();
trash.set_label(&ngettext_f(
trash.set_label(&match finder {
Finder::Exact => ngettext_f(
"Move {n} copy to Trash",
"Move {n} copies to Trash",
n as u64,
&[],
));
),
Finder::Similar => ngettext_f(
"Move {n} photo to Trash",
"Move {n} photos to Trash",
n as u64,
&[],
),
});
trash.set_sensitive(n > 0);
}
};
@@ -171,12 +399,14 @@ fn duplicate_review(ui: &Rc<Ui>, sets: Vec<Vec<PhotoItem>>, trash: &gtk4::Button
}
let ui_trash = ui.clone();
trash.connect_clicked(move |button| {
let handler = trash.connect_clicked(move |button| {
let uids = doomed(&toggles.borrow());
if uids.is_empty() {
return;
}
trash_photos(&ui_trash, uids, true);
// An exact copy is one file of its shot; a similar photo stands for
// its whole shot, raw and all.
trash_photos(&ui_trash, uids, finder == Finder::Exact);
if let Some(dialog) = button
.ancestor(adw::Dialog::static_type())
.and_downcast::<adw::Dialog>()
@@ -190,7 +420,7 @@ fn duplicate_review(ui: &Rc<Ui>, sets: Vec<Vec<PhotoItem>>, trash: &gtk4::Button
.vexpand(true)
.child(&content)
.build();
scroll.upcast()
(scroll.upcast(), handler)
}
/// The uids of every copy not kept.
+10 -2
View File
@@ -11,8 +11,16 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
## [Unreleased]
Schema: **34**. Photos gain a `similar_hash` column for the coming similar-photo finder. It
stays empty until the daemon hashes thumbnails, so updating reads and downloads nothing.
Schema: **34**. Photos gain a `similar_hash` column for the similar-photo finder. It stays
empty until the finder is first opened, so updating reads and downloads nothing.
### Added
- **Similar photos.** The duplicate finder gains a Similar switch that also finds resized or
re-encoded copies and shots of one moment, by comparing the thumbnails. The first search
hashes every thumbnail the server has, with a progress bar; photos without one are not
downloaded and are counted as not compared. Every photo in a set starts ticked Keep, since
photos that look alike can be different shots, and a photo not kept goes to Trash with the
rest of its shot.
## [2.3.0] — 2026-09-28
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: German\n"
@@ -315,7 +315,6 @@ msgstr "Umbenennen"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -767,7 +766,7 @@ msgstr "Das Album konnte nicht erstellt werden"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1908,28 +1907,82 @@ msgstr "Identität übernommen"
msgid "Couldn't adopt that computer"
msgstr "Der Computer konnte nicht übernommen werden"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Duplikate werden gesucht …"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Exakt"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Mehrfach gespeicherte Fotos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Ähnlich"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Fotos, die sich ähneln"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplikate"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Duplikate werden gesucht …"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Ähnliche Fotos werden gesucht …"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Duplikate konnten nicht gesucht werden"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Ähnliche Fotos konnten nicht gesucht werden"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Fotos werden verglichen …"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Fotos werden anhand ihrer Vorschaubilder verglichen. Beim ersten Mal dauert das eine Weile."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} von {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "Keine Duplikate"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Jedes Foto in deiner Mediathek ist nur einmal gespeichert."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "Keine ähnlichen Fotos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "Keine zwei Fotos in deiner Mediathek sehen sich ähnlich."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} Foto konnte nicht verglichen werden, weil es noch kein Vorschaubild hat."
msgstr[1] "{n} Fotos konnten nicht verglichen werden, weil sie noch kein Vorschaubild haben."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1937,21 +1990,40 @@ msgstr ""
"Diese Fotos sind mehrfach gespeichert. Die Kopien, die du nicht behältst, "
"werden in den Papierkorb verschoben, wo du sie wiederherstellen kannst."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Diese Fotos sehen sich ähnlich. Es können Kopien eines Fotos oder verschiedene Aufnahmen eines Moments sein, daher werden alle behalten, bis du sie abwählst. Die Fotos, die du nicht behältst, werden in den Papierkorb verschoben, wo du sie wiederherstellen kannst."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} Kopie"
msgstr[1] "{date}, {n} Kopien"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} Foto"
msgstr[1] "{date}, {n} Fotos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "{n} Kopie in den Papierkorb verschieben"
msgstr[1] "{n} Kopien in den Papierkorb verschieben"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "{n} Foto in den Papierkorb verschieben"
msgstr[1] "{n} Fotos in den Papierkorb verschieben"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Behalten"
@@ -3008,13 +3080,6 @@ msgid_plural "{n} years ago"
msgstr[0] "vor {n} Jahr"
msgstr[1] "vor {n} Jahren"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} Foto"
msgstr[1] "{date}, {n} Fotos"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\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"
@@ -311,7 +311,6 @@ msgstr "Rename"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -759,7 +758,7 @@ msgstr "Couldn't create the album"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1883,28 +1882,82 @@ msgstr "Identity adopted"
msgid "Couldn't adopt that computer"
msgstr "Couldn't adopt that computer"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Looking for duplicates…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Exact"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Photos stored more than once"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Similar"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Photos that look alike"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplicates"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Looking for duplicates…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Looking for similar photos…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Couldn't look for duplicates"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Couldn't look for similar photos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Comparing photos…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Photos are compared by their thumbnails. The first time takes a while."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} of {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "No duplicates"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Every photo in your library is stored once."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "No similar photos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "No two photos in your library look alike."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} photo couldn't be compared because it has no thumbnail yet."
msgstr[1] "{n} photos couldn't be compared because they have no thumbnail yet."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1912,21 +1965,40 @@ msgstr ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} copy"
msgstr[1] "{date}, {n} copies"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} photo"
msgstr[1] "{date}, {n} photos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "Move {n} copy to Trash"
msgstr[1] "Move {n} copies to Trash"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "Move {n} photo to Trash"
msgstr[1] "Move {n} photos to Trash"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Keep"
@@ -2962,13 +3034,6 @@ msgid_plural "{n} years ago"
msgstr[0] "{n} year ago"
msgstr[1] "{n} years ago"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} photo"
msgstr[1] "{date}, {n} photos"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: Spanish\n"
@@ -312,7 +312,6 @@ msgstr "Renombrar"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -763,7 +762,7 @@ msgstr "No se pudo crear el álbum"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1899,28 +1898,82 @@ msgstr "Identidad adoptada"
msgid "Couldn't adopt that computer"
msgstr "No se pudo adoptar ese equipo"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Buscando duplicados…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Exactas"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Fotos guardadas más de una vez"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Similares"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Fotos que se parecen"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplicados"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Buscando duplicados…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Buscando fotos similares…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "No se pudieron buscar duplicados"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "No se pudieron buscar fotos similares"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Comparando fotos…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Las fotos se comparan por sus miniaturas. La primera vez tarda un rato."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} de {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "No hay duplicados"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Cada foto de su biblioteca está guardada una sola vez."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "No hay fotos similares"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "No hay dos fotos de su biblioteca que se parezcan."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} foto no se pudo comparar porque aún no tiene miniatura."
msgstr[1] "{n} fotos no se pudieron comparar porque aún no tienen miniatura."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1928,21 +1981,40 @@ msgstr ""
"Estas fotos están guardadas más de una vez. Las copias que no conserve se "
"moverán a la papelera, donde podrá restaurarlas."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Estas fotos se parecen. Pueden ser copias de una misma foto o tomas distintas de un mismo momento, así que se conservan todas hasta que las desmarque. Las fotos que no conserve se moverán a la papelera, donde podrá restaurarlas."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} copia"
msgstr[1] "{date}, {n} copias"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} fotos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "Mover {n} copia a la papelera"
msgstr[1] "Mover {n} copias a la papelera"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "Mover {n} foto a la papelera"
msgstr[1] "Mover {n} fotos a la papelera"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Conservar"
@@ -2994,13 +3066,6 @@ msgid_plural "{n} years ago"
msgstr[0] "hace {n} año"
msgstr[1] "hace {n} años"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} fotos"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: French\n"
@@ -315,7 +315,6 @@ msgstr "Renommer"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -767,7 +766,7 @@ msgstr "Impossible de créer l’album"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1906,28 +1905,82 @@ msgstr "Identité adoptée"
msgid "Couldn't adopt that computer"
msgstr "Impossible d’adopter cet ordinateur"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Recherche des doublons…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Exacts"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Photos stockées plusieurs fois"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Similaires"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Photos qui se ressemblent"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Doublons"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Recherche des doublons…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Recherche des photos similaires…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Impossible de rechercher les doublons"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Impossible de rechercher les photos similaires"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Comparaison des photos…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Les photos sont comparées d’après leurs miniatures. La première fois prend un moment."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} sur {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "Aucun doublon"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Chaque photo de votre bibliothèque n’est stockée qu’une fois."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "Aucune photo similaire"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "Aucune photo de votre bibliothèque ne ressemble à une autre."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} photo n’a pas pu être comparée, car elle n’a pas encore de miniature."
msgstr[1] "{n} photos n’ont pas pu être comparées, car elles n’ont pas encore de miniature."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1935,21 +1988,40 @@ msgstr ""
"Ces photos sont stockées plusieurs fois. Les copies que vous ne gardez pas "
"sont mises à la corbeille, d’où vous pouvez les restaurer."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Ces photos se ressemblent. Il peut s’agir de copies d’une même photo ou de prises différentes d’un même moment : elles sont donc toutes gardées tant que vous ne les décochez pas. Les photos que vous ne gardez pas sont mises à la corbeille, d’où vous pouvez les restaurer."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} copie"
msgstr[1] "{date}, {n} copies"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} photo"
msgstr[1] "{date}, {n} photos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "Mettre {n} copie à la corbeille"
msgstr[1] "Mettre {n} copies à la corbeille"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "Mettre {n} photo à la corbeille"
msgstr[1] "Mettre {n} photos à la corbeille"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Garder"
@@ -3009,13 +3081,6 @@ msgid_plural "{n} years ago"
msgstr[0] "il y a {n} an"
msgstr[1] "il y a {n} ans"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} photo"
msgstr[1] "{date}, {n} photos"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: Italian\n"
@@ -311,7 +311,6 @@ msgstr "Rinomina"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -763,7 +762,7 @@ msgstr "Impossibile creare l'album"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1904,28 +1903,82 @@ msgstr "Identità adottata"
msgid "Couldn't adopt that computer"
msgstr "Impossibile adottare quel computer"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Ricerca dei duplicati…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Esatti"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Foto salvate più di una volta"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Simili"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Foto che si somigliano"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplicati"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Ricerca dei duplicati…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Ricerca delle foto simili…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Impossibile cercare i duplicati"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Impossibile cercare le foto simili"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Confronto delle foto…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Le foto vengono confrontate in base alle miniature. La prima volta richiede un po’ di tempo."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} di {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "Nessun duplicato"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Ogni foto della libreria è salvata una sola volta."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "Nessuna foto simile"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "Nella libreria non ci sono due foto che si somigliano."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} foto non è stata confrontata perché non ha ancora una miniatura."
msgstr[1] "{n} foto non sono state confrontate perché non hanno ancora una miniatura."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1933,21 +1986,40 @@ msgstr ""
"Queste foto sono salvate più di una volta. Le copie che non tieni vengono "
"spostate nel cestino, da dove puoi ripristinarle."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Queste foto si somigliano. Possono essere copie di una stessa foto o scatti diversi di uno stesso momento, quindi vengono tenute tutte finché non le deselezioni. Le foto che non tieni vengono spostate nel cestino, da dove puoi ripristinarle."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} copia"
msgstr[1] "{date}, {n} copie"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} foto"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "Sposta {n} copia nel cestino"
msgstr[1] "Sposta {n} copie nel cestino"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "Sposta {n} foto nel cestino"
msgstr[1] "Sposta {n} foto nel cestino"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Tieni"
@@ -2998,13 +3070,6 @@ msgid_plural "{n} years ago"
msgstr[0] "{n} anno fa"
msgstr[1] "{n} anni fa"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} foto"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: Dutch\n"
@@ -312,7 +312,6 @@ msgstr "Hernoemen"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -764,7 +763,7 @@ msgstr "Kon het album niet aanmaken"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1902,28 +1901,82 @@ msgstr "Identiteit overgenomen"
msgid "Couldn't adopt that computer"
msgstr "Kon die computer niet overnemen"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Duplicaten zoeken…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Exact"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Foto's die meer dan eens zijn opgeslagen"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Vergelijkbaar"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Foto's die op elkaar lijken"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplicaten"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Duplicaten zoeken…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Vergelijkbare foto's zoeken…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Kon niet naar duplicaten zoeken"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Kon niet naar vergelijkbare foto's zoeken"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Foto's vergelijken…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Foto's worden vergeleken aan de hand van hun miniaturen. De eerste keer duurt dat even."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} van {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "Geen duplicaten"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Elke foto in je bibliotheek is maar één keer opgeslagen."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "Geen vergelijkbare foto's"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "Er zijn geen twee foto's in je bibliotheek die op elkaar lijken."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} foto kon niet worden vergeleken omdat die nog geen miniatuur heeft."
msgstr[1] "{n} foto's konden niet worden vergeleken omdat ze nog geen miniatuur hebben."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1931,21 +1984,40 @@ msgstr ""
"Deze foto's zijn meer dan eens opgeslagen. De kopieën die je niet houdt, "
"gaan naar de prullenbak, waar je ze kunt terugzetten."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Deze foto's lijken op elkaar. Het kunnen kopieën van één foto zijn of verschillende opnamen van één moment, dus ze worden allemaal gehouden tot je ze uitvinkt. De foto's die je niet houdt, gaan naar de prullenbak, waar je ze kunt terugzetten."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} kopie"
msgstr[1] "{date}, {n} kopieën"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} foto's"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "{n} kopie naar prullenbak verplaatsen"
msgstr[1] "{n} kopieën naar prullenbak verplaatsen"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "{n} foto naar prullenbak verplaatsen"
msgstr[1] "{n} foto's naar prullenbak verplaatsen"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Houden"
@@ -2998,13 +3070,6 @@ msgid_plural "{n} years ago"
msgstr[0] "{n} jaar geleden"
msgstr[1] "{n} jaar geleden"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} foto's"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+90 -21
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: proton-drive-linux 2.3.0\n"
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
"POT-Creation-Date: 2026-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\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"
@@ -288,7 +288,6 @@ msgstr ""
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -734,7 +733,7 @@ msgstr ""
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1832,48 +1831,125 @@ msgstr ""
msgid "Couldn't adopt that computer"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr ""
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural ""
"{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] ""
msgstr[1] ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid ""
"These photos look alike. They can be copies of one photo or different shots "
"of one moment, so all of them are kept until you untick them. The photos you "
"don't keep move to Trash, where you can restore them."
msgstr ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] ""
msgstr[1] ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] ""
msgstr[1] ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] ""
msgstr[1] ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] ""
msgstr[1] ""
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr ""
@@ -2879,13 +2955,6 @@ msgid_plural "{n} years ago"
msgstr[0] ""
msgstr[1] ""
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] ""
msgstr[1] ""
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+90 -23
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: Polish\n"
@@ -314,7 +314,6 @@ msgstr "Zmień nazwę"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -771,7 +770,7 @@ msgstr "Nie można utworzyć albumu"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1930,28 +1929,83 @@ msgstr "Przejęto tożsamość"
msgid "Couldn't adopt that computer"
msgstr "Nie można przejąć tożsamości tego komputera"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Wyszukiwanie duplikatów…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Dokładne"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Zdjęcia zapisane więcej niż raz"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Podobne"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Zdjęcia, które wyglądają podobnie"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplikaty"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Wyszukiwanie duplikatów…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Wyszukiwanie podobnych zdjęć…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Nie można wyszukać duplikatów"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Nie można wyszukać podobnych zdjęć"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Porównywanie zdjęć…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "Zdjęcia są porównywane na podstawie miniatur. Za pierwszym razem trwa to chwilę."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} z {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "Brak duplikatów"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Każde zdjęcie w bibliotece jest zapisane tylko raz."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "Brak podobnych zdjęć"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "W bibliotece nie ma dwóch podobnych zdjęć."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "Nie udało się porównać {n} zdjęcia, bo nie ma jeszcze miniatury."
msgstr[1] "Nie udało się porównać {n} zdjęć, bo nie mają jeszcze miniatur."
msgstr[2] "Nie udało się porównać {n} zdjęć, bo nie mają jeszcze miniatur."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1959,7 +2013,11 @@ msgstr ""
"Te zdjęcia są zapisane więcej niż raz. Kopie, których nie zachowasz, trafią "
"do kosza, skąd można je przywrócić."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Te zdjęcia wyglądają podobnie. Mogą to być kopie jednego zdjęcia albo różne ujęcia jednej chwili, dlatego wszystkie są zachowywane, dopóki ich nie odznaczysz. Zdjęcia, których nie zachowasz, trafią do kosza, skąd można je przywrócić."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
@@ -1967,7 +2025,16 @@ msgstr[0] "{date}, {n} kopia"
msgstr[1] "{date}, {n} kopie"
msgstr[2] "{date}, {n} kopii"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} zdjęcie"
msgstr[1] "{date}, {n} zdjęcia"
msgstr[2] "{date}, {n} zdjęć"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
@@ -1975,7 +2042,15 @@ msgstr[0] "Przenieś {n} kopię do kosza"
msgstr[1] "Przenieś {n} kopie do kosza"
msgstr[2] "Przenieś {n} kopii do kosza"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "Przenieś {n} zdjęcie do kosza"
msgstr[1] "Przenieś {n} zdjęcia do kosza"
msgstr[2] "Przenieś {n} zdjęć do kosza"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Zachowaj"
@@ -3034,14 +3109,6 @@ msgstr[0] "{n} rok temu"
msgstr[1] "{n} lata temu"
msgstr[2] "{n} lat temu"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} zdjęcie"
msgstr[1] "{date}, {n} zdjęcia"
msgstr[2] "{date}, {n} zdjęć"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"
+87 -22
View File
@@ -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-09-28 03:11+0200\n"
"POT-Creation-Date: 2026-09-28 03:33+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: Portuguese (Brazil)\n"
@@ -311,7 +311,6 @@ msgstr "Renomear"
#: crates/pdfs-gui/src/app/pages/browser.rs:414
#: crates/pdfs-gui/src/app/pages/browser.rs:1306
#: crates/pdfs-gui/src/app/pages/browser.rs:1338
#: crates/pdfs-gui/src/app/pages/duplicates.rs:24
#: crates/pdfs-gui/src/app/pages/photos.rs:607
#: crates/pdfs-gui/src/app/pages/photos.rs:2063
#: crates/pdfs-gui/src/app/pages/photos.rs:2067
@@ -763,7 +762,7 @@ msgstr "Não foi possível criar o álbum"
#: crates/pdfs-gui/src/app/pages/devices.rs:532
#: crates/pdfs-gui/src/app/pages/devices.rs:682
#: crates/pdfs-gui/src/app/pages/devices.rs:912
#: crates/pdfs-gui/src/app/pages/duplicates.rs:63
#: crates/pdfs-gui/src/app/pages/duplicates.rs:223
#: crates/pdfs-gui/src/app/pages/locations.rs:342
#: crates/pdfs-gui/src/app/pages/locations.rs:516
#: crates/pdfs-gui/src/app/pages/locations.rs:739
@@ -1894,28 +1893,82 @@ msgstr "Identidade adotada"
msgid "Couldn't adopt that computer"
msgstr "Não foi possível adotar aquele computador"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:15
msgid "Looking for duplicates…"
msgstr "Procurando duplicatas…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:79
msgctxt "duplicates"
msgid "Exact"
msgstr "Exatas"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:42
#: crates/pdfs-gui/src/app/pages/duplicates.rs:80
msgid "Photos stored more than once"
msgstr "Fotos armazenadas mais de uma vez"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:84
msgctxt "duplicates"
msgid "Similar"
msgstr "Semelhantes"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:85
msgid "Photos that look alike"
msgstr "Fotos parecidas"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:102
msgid "Duplicates"
msgstr "Duplicatas"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:56
#: crates/pdfs-gui/src/app/pages/duplicates.rs:62
#: crates/pdfs-gui/src/app/pages/duplicates.rs:151
msgid "Looking for duplicates…"
msgstr "Procurando duplicatas…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:152
msgid "Looking for similar photos…"
msgstr "Procurando fotos semelhantes…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:176
msgid "Couldn't look for duplicates"
msgstr "Não foi possível procurar duplicatas"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:69
#: crates/pdfs-gui/src/app/pages/duplicates.rs:177
msgid "Couldn't look for similar photos"
msgstr "Não foi possível procurar fotos semelhantes"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:185
msgid "Comparing photos…"
msgstr "Comparando fotos…"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:187
msgid "Photos are compared by their thumbnails. The first time takes a while."
msgstr "As fotos são comparadas pelas miniaturas. Na primeira vez, isso demora um pouco."
#. Translators: {done} and {total} are numbers of photos.
#: crates/pdfs-gui/src/app/pages/duplicates.rs:196
#, rust-format
msgid "{done} of {total}"
msgstr "{done} de {total}"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:233
msgid "No duplicates"
msgstr "Nenhuma duplicata"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:71
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
msgid "Every photo in your library is stored once."
msgstr "Cada foto da sua biblioteca está armazenada uma única vez."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:88
#: crates/pdfs-gui/src/app/pages/duplicates.rs:239
msgid "No similar photos"
msgstr "Nenhuma foto semelhante"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:241
msgid "No two photos in your library look alike."
msgstr "Não há duas fotos parecidas na sua biblioteca."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:263
#, rust-format
msgid "{n} photo couldn't be compared because it has no thumbnail yet."
msgid_plural "{n} photos couldn't be compared because they have no thumbnail yet."
msgstr[0] "{n} foto não pôde ser comparada porque ainda não tem miniatura."
msgstr[1] "{n} fotos não puderam ser comparadas porque ainda não têm miniatura."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:286
msgid ""
"These photos are stored more than once. The copies you don't keep move to "
"Trash, where you can restore them."
@@ -1923,21 +1976,40 @@ msgstr ""
"Estas fotos estão armazenadas mais de uma vez. As cópias que você não "
"mantiver vão para a lixeira, de onde podem ser restauradas."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:111
#: crates/pdfs-gui/src/app/pages/duplicates.rs:289
msgid "These photos look alike. They can be copies of one photo or different shots of one moment, so all of them are kept until you untick them. The photos you don't keep move to Trash, where you can restore them."
msgstr "Estas fotos são parecidas. Podem ser cópias de uma mesma foto ou cliques diferentes de um mesmo momento, por isso todas são mantidas até você desmarcá-las. As fotos que você não mantiver vão para a lixeira, de onde podem ser restauradas."
#: crates/pdfs-gui/src/app/pages/duplicates.rs:323
#, rust-format
msgid "{date}, {n} copy"
msgid_plural "{date}, {n} copies"
msgstr[0] "{date}, {n} cópia"
msgstr[1] "{date}, {n} cópias"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:143
#: crates/pdfs-gui/src/app/pages/duplicates.rs:330
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} fotos"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:364
#, rust-format
msgid "Move {n} copy to Trash"
msgid_plural "Move {n} copies to Trash"
msgstr[0] "Mover {n} cópia para a lixeira"
msgstr[1] "Mover {n} cópias para a lixeira"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:235
#: crates/pdfs-gui/src/app/pages/duplicates.rs:370
#, rust-format
msgid "Move {n} photo to Trash"
msgid_plural "Move {n} photos to Trash"
msgstr[0] "Mover {n} foto para a lixeira"
msgstr[1] "Mover {n} fotos para a lixeira"
#: crates/pdfs-gui/src/app/pages/duplicates.rs:465
msgid "Keep"
msgstr "Manter"
@@ -2986,13 +3058,6 @@ msgid_plural "{n} years ago"
msgstr[0] "há {n} ano"
msgstr[1] "há {n} anos"
#: crates/pdfs-gui/src/app/pages/photos.rs:2994
#, rust-format
msgid "{date}, {n} photo"
msgid_plural "{date}, {n} photos"
msgstr[0] "{date}, {n} foto"
msgstr[1] "{date}, {n} fotos"
#. Translators: a check item in a photo's menu, on while the photo is a favorite.
#: crates/pdfs-gui/src/app/pages/photos.rs:3231
msgctxt "state"