Compare commits

...

10 commits

Author SHA1 Message Date
2ce31a1a30 Add image annotation / drawing mode
Adds a draw mode toggle (pencil icon, shortcut D) that reveals a
bottom toolbar with freeform pen and shape tools (rectangle, ellipse,
triangle, star), a colour picker, line-width spinner, undo (Ctrl+Z),
clear-all, and a "Save Copy…" button that exports a flattened PNG.

Copying to clipboard (Ctrl+C) also flattens the annotations when any
strokes are present. Strokes are stored in image-pixel coordinates and
rendered via a Cairo overlay in the snapshot pipeline, so they remain
correctly positioned during zoom and pan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-18 09:38:31 +02:00
Roy-Magne Mo
fcf211b7da Update Norwegian Nynorsk translation 2026-09-12 08:42:28 +00:00
Sophie Herold
0ae0d797d1 Release 51.0 2026-09-09 14:06:14 +02:00
Bruce Cowan
4a263ac4db Update British English translation 2026-09-08 18:35:26 +00:00
Balázs Úr
2d0796fa64 Update Hungarian translation 2026-09-08 15:59:17 +00:00
burns
63e9c066e2 Update Brazilian Portuguese translation
Co-authored-by: burns <ahoradocafezinho@gmail.com>
Reviewed-by: Juliano de Souza Camargo <julianosc@protonmail.com>
2026-09-07 02:44:02 +00:00
Daniel Mustieles
9eb557f2eb Updated Spanish translation 2026-09-04 13:18:40 +02:00
Brage Fuglseth
f2a80d3cea Update Norwegian Bokmål translation 2026-09-03 21:18:13 +00:00
Brage Fuglseth
2d2acb13b0 Update Norwegian Bokmål translation 2026-09-03 21:16:46 +00:00
Марко Костић
7aab6d033c Update Serbian translation 2026-09-03 09:39:36 +00:00
21 changed files with 2469 additions and 651 deletions

62
Cargo.lock generated
View file

@ -946,9 +946,9 @@ dependencies = [
[[package]]
name = "glycin"
version = "4.0.0-beta.1"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63b783b3024958cc5dba0844867be5979dba473eced20ec5847c79b1bbaf47"
checksum = "dc9bb9cfa3d4db9eab0e2394f0e60fe4071fd31affd1704f2ecccc6bdd24c7b9"
dependencies = [
"glycin-builtin",
"glycin-external",
@ -956,18 +956,18 @@ dependencies = [
[[package]]
name = "glycin-builtin"
version = "4.0.0-beta.1"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478cc2ec0f9a3eae43485c139e683ac7880d4a9c5157704ab5ab72454e853442"
checksum = "b801b326426a0ccb61899174b0a08c2316defd323cfe78be9aa6232a2efdfeac"
dependencies = [
"glycin-core",
]
[[package]]
name = "glycin-common"
version = "2.0.0-beta.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f682024e248da40a61c1f2176941d7d1492489969765f07ff6afa505a215db"
checksum = "f34692fa7314e5108eafaba7621460d958caba839a6d5040f1caba98d7b4a692"
dependencies = [
"bitflags 2.13.1",
"gufo-common",
@ -984,9 +984,9 @@ dependencies = [
[[package]]
name = "glycin-core"
version = "4.0.0-beta.1"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "935a44dbb054344d24d3d141d63460544b039af8b7cd1e48fc86ce4fc9bc5351"
checksum = "9466e4f18a53312faee414ea85813d6674d4cb5badad059365c4882984b40201"
dependencies = [
"async-fs",
"async-global-executor",
@ -1023,18 +1023,18 @@ dependencies = [
[[package]]
name = "glycin-external"
version = "4.0.0-beta.1"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0966ed359a4824ab4985915e48bf403a35c2826b77eaa4724889162443d65abd"
checksum = "a7d335778b14437e1339d703afd1ce69e61df8ab5420882b4b7821b9a2ca9d4a"
dependencies = [
"glycin-core",
]
[[package]]
name = "glycin-image-rs"
version = "2.2.0-beta.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2f9228ea82df0710f9bd64ec487ebdb224b1749bd24ecb2dc1148e9f935d87"
checksum = "3df0cbc11fe5d5523575b04f724c82d8d50e2c03830622b655e7d4015eba6472"
dependencies = [
"bytemuck",
"exr",
@ -1055,9 +1055,9 @@ dependencies = [
[[package]]
name = "glycin-utils"
version = "5.0.0-beta"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a22f2105cf122cf99304f24f3c2a071942ff86babf15804eb5d6db775e9dd53f"
checksum = "68b112ddb84bd9c1453b119292e38788aa18015d5c12158c1d6fab1b9e353ee7"
dependencies = [
"async-lock",
"bitflags 2.13.1",
@ -1202,9 +1202,9 @@ dependencies = [
[[package]]
name = "gufo"
version = "0.5.0-beta.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dfc7feee4f49b9ba9e056787e45dc183094e82facd30134ac1264250b77e40"
checksum = "6a16d7faeeb03e2925ac6b0f40630b15b1992d08c50add402f35fde495e44f64"
dependencies = [
"chrono",
"gufo-common",
@ -1220,9 +1220,9 @@ dependencies = [
[[package]]
name = "gufo-common"
version = "2.0.0-beta.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7226c8f146ccdea8d7a8cbfa03f7b87372092e0f39655cf19bade7d2fe0c8e"
checksum = "d583cdde9eb6985dbb6e596d27677e5d353d5eee44763cbf7c7ead3147d337bf"
dependencies = [
"chrono",
"paste",
@ -1233,9 +1233,9 @@ dependencies = [
[[package]]
name = "gufo-exif"
version = "0.5.0-beta.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d580a20163c7a7d277c8cf9169dc18deb3003de4f08fb6c56d6c878901dfa0"
checksum = "e99100988e312c22c6f209663b289e727db3918d06fbcbbc2315149457b42b75"
dependencies = [
"chrono",
"gufo-common",
@ -1248,9 +1248,9 @@ dependencies = [
[[package]]
name = "gufo-jpeg"
version = "0.5.0-beta.1"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f970f6a7ec35035ab893b394ced211205747d58c92d3b46665142d275aedca55"
checksum = "383907a5c532bff484b2914d649e368e98ce6426829458353fe40070225b07a5"
dependencies = [
"gufo-common",
"indexmap",
@ -1262,9 +1262,9 @@ dependencies = [
[[package]]
name = "gufo-png"
version = "0.5.0-beta.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57702bf2885289ea8a77a842d2792aca42975f2d1f237d785f45d30e78be363a"
checksum = "e5c3065acab6cf458afbe53f73462a0dbf17348a2af3c889183f8714b3bff6fc"
dependencies = [
"crc32fast",
"encoding_rs",
@ -1276,9 +1276,9 @@ dependencies = [
[[package]]
name = "gufo-tiff"
version = "0.5.0-beta.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf3a0dbc580a3653ac9cc6234d66cddc1fd0af5cc7b6f3cde63c9a2ad70d83e"
checksum = "e9c77c38e7815b76e08b1943dc67517e02543504c14a29e071a4ddf9bee111ec"
dependencies = [
"gufo-common",
"thiserror",
@ -1286,9 +1286,9 @@ dependencies = [
[[package]]
name = "gufo-webp"
version = "0.5.0-beta.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e1998271aa38387aa572f957d4cc7e64841dd34e6354e2008aa8f74f25f0e4c"
checksum = "04d6d3dc0ccd22a30619df68ce347c844d8adb49b822f42660d9a92e6ae14540"
dependencies = [
"gufo-common",
"thiserror",
@ -1296,9 +1296,9 @@ dependencies = [
[[package]]
name = "gufo-xmp"
version = "0.5.0-beta.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d62245a5d579d9748465f91f99df9da6d984c253a8179c8e6d98a267f0f9ff"
checksum = "903aaa13213b860bbad3565e62f3fbd1087ca9404692ebe53728654653470561"
dependencies = [
"chrono",
"gufo-common",
@ -1637,7 +1637,7 @@ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "loupe"
version = "51.0.0-rc"
version = "51.0.0"
dependencies = [
"anyhow",
"arc-swap",

View file

@ -1,6 +1,6 @@
[package]
name = "loupe"
version = "51.0.0-rc"
version = "51.0.0"
edition = "2024"
rust-version = "1.93"
license = "GPL-3.0-or-later"
@ -31,12 +31,12 @@ chrono = { version = "0.4.38", default-features = false, features = ["std"] }
gettext-rs = { version = "0.8.0", features = ["gettext-system"] }
gettext-sys = { version = "0.27.0", features = ["gettext-system"] }
gio = { version = "0.22.0", features = ["v2_74"] }
glycin = { version = "4.0.0-beta.1", features = ["gdk4"] }
glycin = { version = "4.0.0", features = ["gdk4"] }
gtk = { package = "gtk4", version = "0.11.0", features = [
"v4_14",
"xml_validation",
] }
gufo = { version = "0.5.0-beta.1", features = ["chrono"] }
gufo = { version = "0.5.0", features = ["chrono"] }
gvdb-macros = "0.1.6"
indexmap = "2.0.0"
libc = "0.2.158"

2
NEWS
View file

@ -1,3 +1,5 @@
## 51.0 (2026-09-09)
## 51.rc (2026-09-01)
### Fixed

View file

@ -1,7 +1,7 @@
project(
'loupe',
'rust',
version: '51.rc',
version: '51.0',
meson_version: '>= 0.59.0',
default_options: [
'warning_level=2',

1
news.d/51.0/released Normal file
View file

@ -0,0 +1 @@
2026-09-09

View file

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: loupe main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/loupe/-/issues\n"
"POT-Creation-Date: 2026-02-27 20:03+0000\n"
"PO-Revision-Date: 2026-03-09 11:33+0000\n"
"POT-Creation-Date: 2026-09-01 12:44+0000\n"
"PO-Revision-Date: 2026-09-01 21:35+0100\n"
"Last-Translator: Bruce Cowan <bruce@bcowan.me.uk>\n"
"Language-Team: British English <en@li.org>\n"
"Language: en_GB\n"
@ -17,11 +17,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 49.0\n"
"X-Generator: Gtranslator 50.0\n"
#: data/org.gnome.Loupe.desktop.in.in:2
#: data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
#: src/widgets/image_window.rs:898
#: src/widgets/image_window.rs:879
msgid "Image Viewer"
msgstr "Image Viewer"
@ -39,7 +39,8 @@ msgid "Show the image properties sidebar"
msgstr "Show the image properties sidebar"
#. developer_name tag deprecated with Appstream 1.0
#: data/org.gnome.Loupe.metainfo.xml.in.in:8 src/about.rs:35
#: data/org.gnome.Loupe.metainfo.xml.in.in:8
#: data/org.gnome.Loupe.metainfo.xml.in.in:10 src/about.rs:35
msgid "The GNOME Project"
msgstr "The GNOME Project"
@ -51,6 +52,18 @@ msgstr ""
"Explore images, view their metadata, and make quick edits by cropping, "
"rotating, or flipping them."
#: data/org.gnome.Loupe.metainfo.xml.in.in:28
msgid "Main Window"
msgstr "Main Window"
#: data/org.gnome.Loupe.metainfo.xml.in.in:32
msgid "Crop Tool"
msgstr "Crop Tool"
#: data/org.gnome.Loupe.metainfo.xml.in.in:36
msgid "Image Properties Panel"
msgstr "Image Properties Panel"
#: data/resources/shortcuts-dialog.ui:6
msgctxt "shortcut window"
msgid "General Shortcuts"
@ -284,44 +297,85 @@ msgid "Could not list other files in directory."
msgstr "Could not list other files in directory."
#. Translators: Display of the f-number <https://en.wikipedia.org/wiki/F-number>. {} will be replaced with the number
#: src/metadata.rs:256
#: src/metadata.rs:270
msgid "\\u{192}\\u{2215}{}"
msgstr "\\u{192}\\u{2215}{}"
#. Translators: Fractional exposure time (photography) in seconds
#: src/metadata.rs:269
#: src/metadata.rs:283
msgid "1\\u{2215}{}\\u{202F}s"
msgstr "1\\u{2215}{}\\u{202F}s"
#. Translators: Exposure time (photography) in seconds
#: src/metadata.rs:277
#: src/metadata.rs:291
msgid "{}\\u{202F}s"
msgstr "{}\\u{202F}s"
#: src/metadata.rs:291
#: src/metadata.rs:305
msgid "{}\\u{202F}mm"
msgstr "{}\\u{202F}mm"
#. Translators: short for "north" in GPS coordinate
#: src/metadata.rs:331
#: src/metadata.rs:395
msgid "N"
msgstr "N"
#. Translators: short for "south" in GPS coordinate
#: src/metadata.rs:335
#: src/metadata.rs:399
msgid "S"
msgstr "S"
#. Translators: short for "east" in GPS coordinate
#: src/metadata.rs:344
#: src/metadata.rs:408
msgid "E"
msgstr "E"
#. Translators: short for "west" in GPS coordinate
#: src/metadata.rs:348
#: src/metadata.rs:412
msgid "W"
msgstr "W"
#: src/metadata.rs:492
msgid "DCI P3"
msgstr "DCI P3"
#: src/metadata.rs:493
msgid "Display P3"
msgstr "Display P3"
#: src/metadata.rs:494
msgid "Rec.2020"
msgstr "Rec.2020"
#: src/metadata.rs:495
msgid "sRGB"
msgstr "sRGB"
#: src/metadata.rs:496 src/metadata.rs:504
msgid "Unspecified"
msgstr "Unspecified"
#: src/metadata.rs:497 src/metadata.rs:506 src/metadata.rs:516
#, rust-format
msgid "Unknown ({})"
msgstr "Unknown ({})"
#: src/metadata.rs:503
msgid "ICtCp"
msgstr "ICtCp"
#: src/metadata.rs:512
msgid "DCI"
msgstr "DCI"
#: src/metadata.rs:513
msgid "PQ"
msgstr "PQ"
#: src/metadata.rs:514
msgid "Gamma 2.4"
msgstr "Gamma 2.4"
#. / Returns localized date + time format
#. Translators: This is the date and time format we use in metadata output etc.
#. The format has to follow <https://docs.gtk.org/glib/method.DateTime.format.html>
@ -449,32 +503,64 @@ msgid "Crop Selection"
msgstr "Crop Selection"
#. Translators: Filename suffix, {} is replaced by a number
#: src/widgets/edit_window.rs:196
#: src/widgets/edit_window.rs:207
#, rust-format
msgid " (Edited)"
msgid_plural " (Edited {})"
msgstr[0] " (Edited)"
msgstr[1] " (Edited {})"
#: src/widgets/edit_window.rs:290 src/widgets/edit_window.rs:316
#: src/widgets/edit_window.rs:292
msgid "Moving Original to Trash"
msgstr "Moving Original to Wastebasket"
#: src/widgets/edit_window.rs:306 src/widgets/edit_window.rs:335
msgid "Failed to save image."
msgstr "Failed to save image."
#: src/widgets/edit_window.rs:344
#: src/widgets/edit_window.rs:315
msgid "Finalizing Save Operation"
msgstr "Finalising Save Operation"
#: src/widgets/edit_window.rs:367
msgid "Editing Image"
msgstr "Editing Image"
#: src/widgets/edit_window.rs:361
#: src/widgets/edit_window.rs:384
msgid "Failed to edit image."
msgstr "Failed to edit image."
#: src/widgets/edit_window.rs:371 src/widgets/edit_window.rs:394
#: src/widgets/edit_window.rs:391
msgid "Saving Image"
msgstr "Saving Image"
#: src/widgets/edit_window.rs:410
msgid "Failed to Save Image"
msgstr "Failed to Save Image"
#: src/widgets/edit_window.rs:377
msgid "Saving Image"
msgstr "Saving Image"
#: src/widgets/edit_window.rs:526
msgid "Save Changes?"
msgstr "Save Changes?"
#: src/widgets/edit_window.rs:528
msgid ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
msgstr ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
#: src/widgets/edit_window.rs:532 src/widgets/image_window.rs:700
msgid "Cancel"
msgstr "Cancel"
#: src/widgets/edit_window.rs:533
msgid "Discard"
msgstr "Discard"
#: src/widgets/edit_window.rs:534
msgid "Save"
msgstr "Save"
#: src/widgets/edit_window.ui:64
msgid "_Cancel"
@ -609,11 +695,11 @@ msgstr "Rotate Right"
#. Translators: This is a toast notification, informing the user that
#. an image has been set as background.
#: src/widgets/image_view.rs:866
#: src/widgets/image_view.rs:870
msgid "Set as background."
msgstr "Set as background."
#: src/widgets/image_view.rs:872
#: src/widgets/image_view.rs:876
msgid "Could not set background."
msgstr "Could not set background."
@ -649,52 +735,48 @@ msgstr "Custom Zoom Level"
msgid "Zoom to Specified Level"
msgstr "Zoom to Specified Level"
#: src/widgets/image_window.rs:528
#: src/widgets/image_window.rs:509
msgid "Supported image formats"
msgstr "Supported image formats"
#: src/widgets/image_window.rs:534
#: src/widgets/image_window.rs:515
msgid "All files"
msgstr "All files"
#: src/widgets/image_window.rs:541
#: src/widgets/image_window.rs:522
msgid "Open Image"
msgstr "Open Image"
#: src/widgets/image_window.rs:633
#: src/widgets/image_window.rs:614
msgid "Image copied to clipboard"
msgstr "Image copied to clipboard"
#: src/widgets/image_window.rs:654
#: src/widgets/image_window.rs:635
msgid "Image moved to trash"
msgstr "Image moved to wastebasket"
#: src/widgets/image_window.rs:655
#: src/widgets/image_window.rs:636
msgid "Undo"
msgstr "Undo"
#: src/widgets/image_window.rs:685
#: src/widgets/image_window.rs:666
msgid "Failed to move image to trash"
msgstr "Failed to move image to wastebasket"
#: src/widgets/image_window.rs:709
#: src/widgets/image_window.rs:690
msgid "Permanently Delete Image?"
msgstr "Permanently Delete Image?"
#: src/widgets/image_window.rs:711
#: src/widgets/image_window.rs:692
#, rust-format
msgid "After deleting the image “{}” it will be permanently lost."
msgstr "After deleting the image “{}” it will be permanently lost."
#: src/widgets/image_window.rs:719
msgid "Cancel"
msgstr "Cancel"
#: src/widgets/image_window.rs:720
#: src/widgets/image_window.rs:701
msgid "Delete"
msgstr "Delete"
#: src/widgets/image_window.rs:731
#: src/widgets/image_window.rs:712
msgid "Failed to delete image"
msgstr "Failed to delete image"
@ -782,24 +864,24 @@ msgid "px"
msgstr "px"
#. Translators: {} is a placeholder for the filename
#: src/widgets/print.rs:364
#: src/widgets/print.rs:365
#, rust-format
msgid "Print “{}”"
msgstr "Print “{}”"
#: src/widgets/print.rs:477
#: src/widgets/print.rs:472
msgid "Continue…"
msgstr "Continue…"
#: src/widgets/print.rs:478
#: src/widgets/print.rs:473
msgid "Print Image"
msgstr "Print Image"
#: src/widgets/print.rs:797
#: src/widgets/print.rs:801
msgid "_Scale"
msgstr "_Scale"
#: src/widgets/print.rs:800
#: src/widgets/print.rs:804
msgid "_Width"
msgstr "_Width"
@ -864,30 +946,72 @@ msgstr "_Fill Space"
msgid "H_eight"
msgstr "H_eight"
#: src/widgets/properties_view.rs:258
#: src/widgets/properties_view.rs:284
msgid "Unknown"
msgstr "Unknown"
#. Translators: Addition of image being transparent to format name
#: src/widgets/properties_view.rs:262
#: src/widgets/properties_view.rs:288
msgid ", transparent"
msgstr ", transparent"
#. Translators: Addition of image being grayscale to format name
#: src/widgets/properties_view.rs:267
#: src/widgets/properties_view.rs:293
msgid ", grayscale"
msgstr ", greyscale"
#. Translators: Addition of bit size to format name
#: src/widgets/properties_view.rs:272
#: src/widgets/properties_view.rs:298
msgid ", {}\\u{202F}bit"
msgstr ", {}\\u{202F}bit"
#: src/widgets/properties_view.rs:290
#: src/widgets/properties_view.rs:315
msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgstr "{}\\u{202F}\\u{D7}\\u{202F}{}"
#: src/widgets/properties_view.rs:326
#. Translators: Shorthand for centimeter
#: src/widgets/properties_view.rs:354
msgid " cm"
msgstr " cm"
#. Translators: Shorthand for inch
#: src/widgets/properties_view.rs:358
msgid "″"
msgstr "″"
#. Translators: Shorthand for meter
#: src/widgets/properties_view.rs:362
msgid " m"
msgstr " m"
#. Translators: Shorthand for millimeter
#: src/widgets/properties_view.rs:366
msgid " mm"
msgstr " mm"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:370
msgid " pc"
msgstr " pc"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:374
msgid " pt"
msgstr " pt"
#: src/widgets/properties_view.rs:386
msgid "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
msgstr "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
#: src/widgets/properties_view.rs:403
msgid "{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:407
msgid "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:435
msgid "Image Coordinates Copied"
msgstr "Image Coordinates Copied"
@ -904,62 +1028,94 @@ msgid "URI"
msgstr "URI"
#: src/widgets/properties_view.ui:66
msgid "Physical Size"
msgstr "Physical Size"
#: src/widgets/properties_view.ui:76
msgid "Resolution"
msgstr "Resolution"
#: src/widgets/properties_view.ui:86
msgid "Image Format"
msgstr "Image Format"
#: src/widgets/properties_view.ui:76
#: src/widgets/properties_view.ui:96
msgid "File Size"
msgstr "File Size"
#: src/widgets/properties_view.ui:90
#: src/widgets/properties_view.ui:110
msgid "File Created"
msgstr "File Created"
#: src/widgets/properties_view.ui:100
#: src/widgets/properties_view.ui:120
msgid "File Modified"
msgstr "File Modified"
#. Translators: This is a label for GPS coordinates
#: src/widgets/properties_view.ui:114
#: src/widgets/properties_view.ui:134
msgid "Location"
msgstr "Location"
#: src/widgets/properties_view.ui:125
#: src/widgets/properties_view.ui:145
msgid "Copy Coordinates"
msgstr "Copy Coordinates"
#: src/widgets/properties_view.ui:136
#: src/widgets/properties_view.ui:156
msgid "Open Location"
msgstr "Open Location"
#: src/widgets/properties_view.ui:149
#: src/widgets/properties_view.ui:169
msgid "Originally Created"
msgstr "Originally Created"
#: src/widgets/properties_view.ui:159
#: src/widgets/properties_view.ui:179
msgid "Creator"
msgstr "Creator"
#: src/widgets/properties_view.ui:189
msgid "Copyright"
msgstr "Copyright"
#: src/widgets/properties_view.ui:199
msgid "Comment"
msgstr "Comment"
#: src/widgets/properties_view.ui:169
#: src/widgets/properties_view.ui:209
msgid "Aperture"
msgstr "Aperture"
#: src/widgets/properties_view.ui:179
#: src/widgets/properties_view.ui:219
msgid "Exposure"
msgstr "Exposure"
#: src/widgets/properties_view.ui:189
#: src/widgets/properties_view.ui:229
msgid "ISO"
msgstr "ISO"
#: src/widgets/properties_view.ui:199
#: src/widgets/properties_view.ui:239
msgid "Focal Length"
msgstr "Focal Length"
#: src/widgets/properties_view.ui:209
#: src/widgets/properties_view.ui:249
msgid "Maker, Model"
msgstr "Maker, Model"
#: src/widgets/window.rs:206
#: src/widgets/properties_view.ui:259
msgid "Lens"
msgstr "Lens"
#: src/widgets/properties_view.ui:269
msgid "Software"
msgstr "Software"
#: src/widgets/properties_view.ui:279
msgid "ICC Profile"
msgstr "ICC Profile"
#: src/widgets/properties_view.ui:289
msgid "CICP"
msgstr "CICP"
#: src/widgets/window.rs:254
msgid "Show Details"
msgstr "Show Details"

297
po/es.po
View file

@ -2,27 +2,26 @@
# Copyright (C) 2023 Loupe's COPYRIGHT HOLDER
# This file is distributed under the same license as the Loupe package.
# Julián Villodre <jvillodrede@gmail.com>, 2024.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2023-2024.
# Daniel Mustieles García <daniel.mustieles@gmail.com>, 2024-2026.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2024-2026.
#
msgid ""
msgstr ""
"Project-Id-Version: Loupe main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/loupe/-/issues\n"
"POT-Creation-Date: 2026-01-30 22:01+0000\n"
"PO-Revision-Date: 2026-02-23 15:46+0100\n"
"Last-Translator: Daniel Mustieles García <daniel.mustieles@gmail.com>\n"
"Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n"
"Language: es_ES\n"
"POT-Creation-Date: 2026-09-01 12:44+0000\n"
"PO-Revision-Date: 2026-09-04 13:13+0200\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Spanish <l10n-es@gnome.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 49.0\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 50.0\n"
#: data/org.gnome.Loupe.desktop.in.in:2
#: data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
#: src/widgets/image_window.rs:897
#: src/widgets/image_window.rs:879
msgid "Image Viewer"
msgstr "Visor de imágenes"
@ -40,7 +39,8 @@ msgid "Show the image properties sidebar"
msgstr "Mostrar la barra lateral de propiedades de la imagen"
#. developer_name tag deprecated with Appstream 1.0
#: data/org.gnome.Loupe.metainfo.xml.in.in:8 src/about.rs:35
#: data/org.gnome.Loupe.metainfo.xml.in.in:8
#: data/org.gnome.Loupe.metainfo.xml.in.in:10 src/about.rs:35
msgid "The GNOME Project"
msgstr "El Proyecto GNOME"
@ -52,6 +52,18 @@ msgstr ""
"Explore imágenes, vea sus metadatos y realice ediciones rápidas "
"recortándolas, rotándolas o volteándolas."
#: data/org.gnome.Loupe.metainfo.xml.in.in:28
msgid "Main Window"
msgstr "Ventana principal"
#: data/org.gnome.Loupe.metainfo.xml.in.in:32
msgid "Crop Tool"
msgstr "Herramienta de recorte"
#: data/org.gnome.Loupe.metainfo.xml.in.in:36
msgid "Image Properties Panel"
msgstr "Panel de propiedades de imagen"
#: data/resources/shortcuts-dialog.ui:6
msgctxt "shortcut window"
msgid "General Shortcuts"
@ -288,44 +300,85 @@ msgstr "No se pudieron listar los archivos de la carpeta"
#. Translators: Display of the f-number
#. <https://en.wikipedia.org/wiki/F-number>. {} will be replaced with the
#. number
#: src/metadata.rs:256
#: src/metadata.rs:270
msgid "\\u{192}\\u{2215}{}"
msgstr "\\u{192}\\u{2215}{}"
#. Translators: Fractional exposure time (photography) in seconds
#: src/metadata.rs:269
#: src/metadata.rs:283
msgid "1\\u{2215}{}\\u{202F}s"
msgstr "1\\u{2215}{}\\u{202F}s"
#. Translators: Exposure time (photography) in seconds
#: src/metadata.rs:277
#: src/metadata.rs:291
msgid "{}\\u{202F}s"
msgstr "{}\\u{202F}s"
#: src/metadata.rs:291
#: src/metadata.rs:305
msgid "{}\\u{202F}mm"
msgstr "{}\\u{202F}mm"
#. Translators: short for "north" in GPS coordinate
#: src/metadata.rs:331
#: src/metadata.rs:395
msgid "N"
msgstr "N"
#. Translators: short for "south" in GPS coordinate
#: src/metadata.rs:335
#: src/metadata.rs:399
msgid "S"
msgstr "S"
#. Translators: short for "east" in GPS coordinate
#: src/metadata.rs:344
#: src/metadata.rs:408
msgid "E"
msgstr "E"
#. Translators: short for "west" in GPS coordinate
#: src/metadata.rs:348
#: src/metadata.rs:412
msgid "W"
msgstr "O"
#: src/metadata.rs:492
msgid "DCI P3"
msgstr "DCI P3"
#: src/metadata.rs:493
msgid "Display P3"
msgstr "Display P3"
#: src/metadata.rs:494
msgid "Rec.2020"
msgstr "Rec.2020"
#: src/metadata.rs:495
msgid "sRGB"
msgstr "sRGB"
#: src/metadata.rs:496 src/metadata.rs:504
msgid "Unspecified"
msgstr "No especificado"
#: src/metadata.rs:497 src/metadata.rs:506 src/metadata.rs:516
#, rust-format
msgid "Unknown ({})"
msgstr "Desconocido ({})"
#: src/metadata.rs:503
msgid "ICtCp"
msgstr "ICtCp"
#: src/metadata.rs:512
msgid "DCI"
msgstr "DCI"
#: src/metadata.rs:513
msgid "PQ"
msgstr "PQ"
#: src/metadata.rs:514
msgid "Gamma 2.4"
msgstr "Gamma 2.4"
#. / Returns localized date + time format
#. Translators: This is the date and time format we use in metadata output
#. etc.
@ -455,32 +508,64 @@ msgid "Crop Selection"
msgstr "Recortar la selección"
#. Translators: Filename suffix, {} is replaced by a number
#: src/widgets/edit_window.rs:195
#: src/widgets/edit_window.rs:207
#, rust-format
msgid " (Edited)"
msgid_plural " (Edited {})"
msgstr[0] " (Editado)"
msgstr[1] " (Editado {})"
#: src/widgets/edit_window.rs:289 src/widgets/edit_window.rs:315
#: src/widgets/edit_window.rs:292
msgid "Moving Original to Trash"
msgstr "Mover el original a la papelera"
#: src/widgets/edit_window.rs:306 src/widgets/edit_window.rs:335
msgid "Failed to save image."
msgstr "Falló al guardar la imagen."
#: src/widgets/edit_window.rs:343
#: src/widgets/edit_window.rs:315
msgid "Finalizing Save Operation"
msgstr "Finalizando la operación de guardar"
#: src/widgets/edit_window.rs:367
msgid "Editing Image"
msgstr "Editando la imagen"
#: src/widgets/edit_window.rs:360
#: src/widgets/edit_window.rs:384
msgid "Failed to edit image."
msgstr "Falló al editar la imagen."
#: src/widgets/edit_window.rs:370 src/widgets/edit_window.rs:393
#: src/widgets/edit_window.rs:391
msgid "Saving Image"
msgstr "Guardando imagen"
#: src/widgets/edit_window.rs:410
msgid "Failed to Save Image"
msgstr "Falló al guardar la imagen."
#: src/widgets/edit_window.rs:376
msgid "Saving Image"
msgstr "Guardando imagen"
#: src/widgets/edit_window.rs:526
msgid "Save Changes?"
msgstr "¿Guardar cambios?"
#: src/widgets/edit_window.rs:528
msgid ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
msgstr ""
"Hay cambios de edición de imágenes no guardados. Cerrar sin guardar perderá "
"permanentemente estas modificaciones."
#: src/widgets/edit_window.rs:532 src/widgets/image_window.rs:700
msgid "Cancel"
msgstr "Cancelar"
#: src/widgets/edit_window.rs:533
msgid "Discard"
msgstr "Descartar"
#: src/widgets/edit_window.rs:534
msgid "Save"
msgstr "Guardar"
#: src/widgets/edit_window.ui:64
msgid "_Cancel"
@ -614,11 +699,11 @@ msgstr "Rotar a la derecha"
#. Translators: This is a toast notification, informing the user that
#. an image has been set as background.
#: src/widgets/image_view.rs:866
#: src/widgets/image_view.rs:870
msgid "Set as background."
msgstr "Establecer como fondo."
#: src/widgets/image_view.rs:872
#: src/widgets/image_view.rs:876
msgid "Could not set background."
msgstr "No se pudo establecer como fondo"
@ -654,52 +739,48 @@ msgstr "Nivel de ampliación personalizado"
msgid "Zoom to Specified Level"
msgstr "Ampliar al nivel específico"
#: src/widgets/image_window.rs:527
#: src/widgets/image_window.rs:509
msgid "Supported image formats"
msgstr "Formatos de imagen soportados"
#: src/widgets/image_window.rs:533
#: src/widgets/image_window.rs:515
msgid "All files"
msgstr "Todos los archivos"
#: src/widgets/image_window.rs:540
#: src/widgets/image_window.rs:522
msgid "Open Image"
msgstr "Abrir imagen"
#: src/widgets/image_window.rs:632
#: src/widgets/image_window.rs:614
msgid "Image copied to clipboard"
msgstr "Imagen copiada al portapapeles"
#: src/widgets/image_window.rs:653
#: src/widgets/image_window.rs:635
msgid "Image moved to trash"
msgstr "Imagen movida a la papelera"
#: src/widgets/image_window.rs:654
#: src/widgets/image_window.rs:636
msgid "Undo"
msgstr "Deshacer"
#: src/widgets/image_window.rs:684
#: src/widgets/image_window.rs:666
msgid "Failed to move image to trash"
msgstr "Falló al mover la imagen a la papelera"
#: src/widgets/image_window.rs:708
#: src/widgets/image_window.rs:690
msgid "Permanently Delete Image?"
msgstr "¿Quiere eliminar la imagen de manera permanente?"
#: src/widgets/image_window.rs:710
#: src/widgets/image_window.rs:692
#, rust-format
msgid "After deleting the image “{}” it will be permanently lost."
msgstr "Después de eliminar la imagen \"{}\" se perderá para siempre."
#: src/widgets/image_window.rs:718
msgid "Cancel"
msgstr "Cancelar"
#: src/widgets/image_window.rs:719
#: src/widgets/image_window.rs:701
msgid "Delete"
msgstr "Eliminar"
#: src/widgets/image_window.rs:730
#: src/widgets/image_window.rs:712
msgid "Failed to delete image"
msgstr "Falló al eliminar la imagen"
@ -787,24 +868,24 @@ msgid "px"
msgstr "px"
#. Translators: {} is a placeholder for the filename
#: src/widgets/print.rs:364
#: src/widgets/print.rs:365
#, rust-format
msgid "Print “{}”"
msgstr "Imprimir «{}»"
#: src/widgets/print.rs:477
#: src/widgets/print.rs:472
msgid "Continue…"
msgstr "Continuar…"
#: src/widgets/print.rs:478
#: src/widgets/print.rs:473
msgid "Print Image"
msgstr "Imprimir imagen"
#: src/widgets/print.rs:797
#: src/widgets/print.rs:801
msgid "_Scale"
msgstr "_Escala"
#: src/widgets/print.rs:800
#: src/widgets/print.rs:804
msgid "_Width"
msgstr "_Anchura"
@ -869,30 +950,72 @@ msgstr "_Rellenar espacio"
msgid "H_eight"
msgstr "A_ltura"
#: src/widgets/properties_view.rs:258
#: src/widgets/properties_view.rs:284
msgid "Unknown"
msgstr "Desconocido"
#. Translators: Addition of image being transparent to format name
#: src/widgets/properties_view.rs:262
#: src/widgets/properties_view.rs:288
msgid ", transparent"
msgstr ", transparente"
#. Translators: Addition of image being grayscale to format name
#: src/widgets/properties_view.rs:267
#: src/widgets/properties_view.rs:293
msgid ", grayscale"
msgstr ", escala de grises"
#. Translators: Addition of bit size to format name
#: src/widgets/properties_view.rs:272
#: src/widgets/properties_view.rs:298
msgid ", {}\\u{202F}bit"
msgstr ", {}\\u{202F}bit"
#: src/widgets/properties_view.rs:290
#: src/widgets/properties_view.rs:315
msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgstr "{}\\u{202F}\\u{D7}\\u{202F}{}"
#: src/widgets/properties_view.rs:326
#. Translators: Shorthand for centimeter
#: src/widgets/properties_view.rs:354
msgid " cm"
msgstr " cm"
#. Translators: Shorthand for inch
#: src/widgets/properties_view.rs:358
msgid "″"
msgstr "″"
#. Translators: Shorthand for meter
#: src/widgets/properties_view.rs:362
msgid " m"
msgstr " m"
#. Translators: Shorthand for millimeter
#: src/widgets/properties_view.rs:366
msgid " mm"
msgstr " mm"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:370
msgid " pc"
msgstr " pc"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:374
msgid " pt"
msgstr " pt"
#: src/widgets/properties_view.rs:386
msgid "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
msgstr "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
#: src/widgets/properties_view.rs:403
msgid "{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:407
msgid "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:435
msgid "Image Coordinates Copied"
msgstr "Coordenadas de la imagen copiadas"
@ -909,63 +1032,95 @@ msgid "URI"
msgstr "URI"
#: src/widgets/properties_view.ui:66
msgid "Physical Size"
msgstr "Tamaño físico"
#: src/widgets/properties_view.ui:76
msgid "Resolution"
msgstr "Resolución"
#: src/widgets/properties_view.ui:86
msgid "Image Format"
msgstr "Formato de la imagen"
#: src/widgets/properties_view.ui:76
#: src/widgets/properties_view.ui:96
msgid "File Size"
msgstr "Tamaño del archivo"
#: src/widgets/properties_view.ui:90
#: src/widgets/properties_view.ui:110
msgid "File Created"
msgstr "Fecha de creación"
#: src/widgets/properties_view.ui:100
#: src/widgets/properties_view.ui:120
msgid "File Modified"
msgstr "Fecha de modificación"
#. Translators: This is a label for GPS coordinates
#: src/widgets/properties_view.ui:114
#: src/widgets/properties_view.ui:134
msgid "Location"
msgstr "Ubicación"
#: src/widgets/properties_view.ui:125
#: src/widgets/properties_view.ui:145
msgid "Copy Coordinates"
msgstr "Copiar coordenadas"
#: src/widgets/properties_view.ui:136
#: src/widgets/properties_view.ui:156
msgid "Open Location"
msgstr "Abrir ubicación"
#: src/widgets/properties_view.ui:149
#: src/widgets/properties_view.ui:169
msgid "Originally Created"
msgstr "Creado originalmente"
#: src/widgets/properties_view.ui:159
#: src/widgets/properties_view.ui:179
msgid "Creator"
msgstr "Creador"
#: src/widgets/properties_view.ui:189
msgid "Copyright"
msgstr "Derechos de autor"
#: src/widgets/properties_view.ui:199
msgid "Comment"
msgstr "Comentar"
#: src/widgets/properties_view.ui:169
#: src/widgets/properties_view.ui:209
msgid "Aperture"
msgstr "Apertura"
#: src/widgets/properties_view.ui:179
#: src/widgets/properties_view.ui:219
msgid "Exposure"
msgstr "Exposición"
#: src/widgets/properties_view.ui:189
#: src/widgets/properties_view.ui:229
msgid "ISO"
msgstr "ISO"
#: src/widgets/properties_view.ui:199
#: src/widgets/properties_view.ui:239
msgid "Focal Length"
msgstr "Longitud focal"
#: src/widgets/properties_view.ui:209
#: src/widgets/properties_view.ui:249
msgid "Maker, Model"
msgstr "Creador, modelo"
#: src/widgets/window.rs:206
#: src/widgets/properties_view.ui:259
msgid "Lens"
msgstr "Lente"
#: src/widgets/properties_view.ui:269
msgid "Software"
msgstr "Software"
#: src/widgets/properties_view.ui:279
msgid "ICC Profile"
msgstr "Perfil ICC"
#: src/widgets/properties_view.ui:289
msgid "CICP"
msgstr "CICP"
#: src/widgets/window.rs:254
msgid "Show Details"
msgstr "Mostrar detalles"
@ -994,10 +1149,6 @@ msgstr "Mostrar detalles"
#~ msgid "Gestures"
#~ msgstr "gestos"
#, fuzzy
#~ msgid "{} %"
#~ msgstr "{}%"
#, fuzzy
#~ msgid "Copy to Clipboard"
#~ msgstr "Copiar al portapapeles"

303
po/hu.po
View file

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Loupe main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/loupe/-/issues\n"
"POT-Creation-Date: 2026-02-27 20:03+0000\n"
"PO-Revision-Date: 2026-03-01 14:33+0100\n"
"POT-Creation-Date: 2026-09-01 12:44+0000\n"
"PO-Revision-Date: 2026-09-08 17:58+0200\n"
"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n"
"Language-Team: Hungarian <openscope at fsf dot hu>\n"
"Language: hu\n"
@ -17,17 +17,16 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 25.08.1\n"
"X-Generator: Lokalize 25.12.3\n"
#: data/org.gnome.Loupe.desktop.in.in:2
#: data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
#: src/widgets/image_window.rs:898
#: src/widgets/image_window.rs:879
msgid "Image Viewer"
msgstr "Képmegjelenítő"
#: data/org.gnome.Loupe.desktop.in.in:3
#: data/org.gnome.Loupe.metainfo.xml.in.in:13
#| msgid "View images"
msgid "View and edit images"
msgstr "Képek megtekintése és szerkesztése"
@ -40,7 +39,8 @@ msgid "Show the image properties sidebar"
msgstr "Képtulajdonságok oldalsáv megjelenítése"
#. developer_name tag deprecated with Appstream 1.0
#: data/org.gnome.Loupe.metainfo.xml.in.in:8 src/about.rs:35
#: data/org.gnome.Loupe.metainfo.xml.in.in:8
#: data/org.gnome.Loupe.metainfo.xml.in.in:10 src/about.rs:35
msgid "The GNOME Project"
msgstr "A GNOME projekt"
@ -52,6 +52,21 @@ msgstr ""
"Képek felfedezése, metaadatok megtekintése és gyors szerkesztések elvégzése "
"levágással, forgatással vagy tükrözéssel."
#: data/org.gnome.Loupe.metainfo.xml.in.in:28
#| msgctxt "shortcut window"
#| msgid "New Window"
msgid "Main Window"
msgstr "Főablak"
#: data/org.gnome.Loupe.metainfo.xml.in.in:32
msgid "Crop Tool"
msgstr "Levágási eszköz"
#: data/org.gnome.Loupe.metainfo.xml.in.in:36
#| msgid "Image Properties"
msgid "Image Properties Panel"
msgstr "Képtulajdonságok panel"
#: data/resources/shortcuts-dialog.ui:6
msgctxt "shortcut window"
msgid "General Shortcuts"
@ -285,44 +300,86 @@ msgid "Could not list other files in directory."
msgstr "Nem sikerült felsorolni egyéb fájlokat a könyvtárban."
#. Translators: Display of the f-number <https://en.wikipedia.org/wiki/F-number>. {} will be replaced with the number
#: src/metadata.rs:256
#: src/metadata.rs:270
msgid "\\u{192}\\u{2215}{}"
msgstr "\\u{192}\\u{2215}{}"
#. Translators: Fractional exposure time (photography) in seconds
#: src/metadata.rs:269
#: src/metadata.rs:283
msgid "1\\u{2215}{}\\u{202F}s"
msgstr "1\\u{2215}{}\\u{202F} mp"
#. Translators: Exposure time (photography) in seconds
#: src/metadata.rs:277
#: src/metadata.rs:291
msgid "{}\\u{202F}s"
msgstr "{}\\u{202F} mp"
#: src/metadata.rs:291
#: src/metadata.rs:305
msgid "{}\\u{202F}mm"
msgstr "{}\\u{202F} mm"
#. Translators: short for "north" in GPS coordinate
#: src/metadata.rs:331
#: src/metadata.rs:395
msgid "N"
msgstr "É"
#. Translators: short for "south" in GPS coordinate
#: src/metadata.rs:335
#: src/metadata.rs:399
msgid "S"
msgstr "D"
#. Translators: short for "east" in GPS coordinate
#: src/metadata.rs:344
#: src/metadata.rs:408
msgid "E"
msgstr "K"
#. Translators: short for "west" in GPS coordinate
#: src/metadata.rs:348
#: src/metadata.rs:412
msgid "W"
msgstr "Ny"
#: src/metadata.rs:492
msgid "DCI P3"
msgstr "DCI P3"
#: src/metadata.rs:493
msgid "Display P3"
msgstr "Display P3"
#: src/metadata.rs:494
msgid "Rec.2020"
msgstr "Rec.2020"
#: src/metadata.rs:495
msgid "sRGB"
msgstr "sRGB"
#: src/metadata.rs:496 src/metadata.rs:504
msgid "Unspecified"
msgstr "Meghatározatlan"
#: src/metadata.rs:497 src/metadata.rs:506 src/metadata.rs:516
#, rust-format
#| msgid "Unknown"
msgid "Unknown ({})"
msgstr "Ismeretlen ({})"
#: src/metadata.rs:503
msgid "ICtCp"
msgstr "ICtCp"
#: src/metadata.rs:512
msgid "DCI"
msgstr "DCI"
#: src/metadata.rs:513
msgid "PQ"
msgstr "PQ"
#: src/metadata.rs:514
msgid "Gamma 2.4"
msgstr "Gamma 2.4"
#. / Returns localized date + time format
#. Translators: This is the date and time format we use in metadata output etc.
#. The format has to follow <https://docs.gtk.org/glib/method.DateTime.format.html>
@ -450,31 +507,67 @@ msgid "Crop Selection"
msgstr "Kijelölés levágása"
#. Translators: Filename suffix, {} is replaced by a number
#: src/widgets/edit_window.rs:196
#: src/widgets/edit_window.rs:207
#, rust-format
msgid " (Edited)"
msgid_plural " (Edited {})"
msgstr[0] " (szerkesztve)"
msgstr[1] " (szerkesztve - {})"
#: src/widgets/edit_window.rs:290 src/widgets/edit_window.rs:316
#: src/widgets/edit_window.rs:292
#| msgctxt "shortcut window"
#| msgid "Move Image to Trash"
msgid "Moving Original to Trash"
msgstr "Eredeti áthelyezése a kukába"
#: src/widgets/edit_window.rs:306 src/widgets/edit_window.rs:335
msgid "Failed to save image."
msgstr "Nem sikerült elmenteni a képet."
#: src/widgets/edit_window.rs:344
#: src/widgets/edit_window.rs:315
msgid "Finalizing Save Operation"
msgstr "Mentési művelet befejezése"
#: src/widgets/edit_window.rs:367
msgid "Editing Image"
msgstr "Kép szerkesztése"
#: src/widgets/edit_window.rs:361
#: src/widgets/edit_window.rs:384
msgid "Failed to edit image."
msgstr "Nem sikerült szerkeszteni a képet."
#: src/widgets/edit_window.rs:371 src/widgets/edit_window.rs:394
#: src/widgets/edit_window.rs:391
msgid "Saving Image"
msgstr "Kép mentése"
#: src/widgets/edit_window.rs:410
msgid "Failed to Save Image"
msgstr "Nem sikerült elmenteni a képet"
#: src/widgets/edit_window.rs:377
msgid "Saving Image"
msgstr "Kép mentése"
#: src/widgets/edit_window.rs:526
msgid "Save Changes?"
msgstr "Elmenti a változtatásokat?"
#: src/widgets/edit_window.rs:528
msgid ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
msgstr ""
"Mentetlen képszerkesztési változtatások találhatók. A mentés nélküli bezárás v"
"églegesen el fogja veszíteni ezeket a módosításokat."
#: src/widgets/edit_window.rs:532 src/widgets/image_window.rs:700
msgid "Cancel"
msgstr "Mégse"
#: src/widgets/edit_window.rs:533
msgid "Discard"
msgstr "Elvetés"
#: src/widgets/edit_window.rs:534
#| msgid "_Save"
msgid "Save"
msgstr "Mentés"
#: src/widgets/edit_window.ui:64
msgid "_Cancel"
@ -535,6 +628,7 @@ msgid "Report _Issue"
msgstr "_Probléma jelentése"
#: src/widgets/image_page.rs:225
#, rust-format
msgid ""
"No image loaders available. Maybe the “glycin-loaders” package with "
"compatibility version “{}” is not installed."
@ -543,10 +637,12 @@ msgstr ""
"rendelkező „glycin-loaders” csomag nincs telepítve."
#: src/widgets/image_page.rs:243
#, rust-format
msgid "The image format “{} ({})” is known but not installed."
msgstr "A(z) „{} ({})” képformátum ismert, de nincs telepítve."
#: src/widgets/image_page.rs:251
#, rust-format
msgid "Unknown image format “{} ({}).”"
msgstr "Ismeretlen képformátum: „{} ({}).”"
@ -555,7 +651,7 @@ msgid "There is not enough free memory available to load this image."
msgstr "Nincs elég elérhető szabad memória a kép betöltéséhez."
#: src/widgets/image_page.rs:267
#| msgid "Failed to save image."
#, rust-format
msgid "Failed to load image file: {}"
msgstr "Nem sikerült betölteni a képfájlt: {}"
@ -605,11 +701,11 @@ msgstr "Forgatás jobbra"
#. Translators: This is a toast notification, informing the user that
#. an image has been set as background.
#: src/widgets/image_view.rs:866
#: src/widgets/image_view.rs:870
msgid "Set as background."
msgstr "Beállítás háttérként."
#: src/widgets/image_view.rs:872
#: src/widgets/image_view.rs:876
msgid "Could not set background."
msgstr "Nem sikerült beállítani a hátteret."
@ -645,51 +741,48 @@ msgstr "Egyéni nagyítási szint"
msgid "Zoom to Specified Level"
msgstr "Nagyítás a megadott szintre"
#: src/widgets/image_window.rs:528
#: src/widgets/image_window.rs:509
msgid "Supported image formats"
msgstr "Támogatott képformátumok"
#: src/widgets/image_window.rs:534
#: src/widgets/image_window.rs:515
msgid "All files"
msgstr "Összes fájl"
#: src/widgets/image_window.rs:541
#: src/widgets/image_window.rs:522
msgid "Open Image"
msgstr "Kép megnyitása"
#: src/widgets/image_window.rs:633
#: src/widgets/image_window.rs:614
msgid "Image copied to clipboard"
msgstr "A kép lemásolva a vágólapra"
#: src/widgets/image_window.rs:654
#: src/widgets/image_window.rs:635
msgid "Image moved to trash"
msgstr "A kép áthelyezve a kukába"
#: src/widgets/image_window.rs:655
#: src/widgets/image_window.rs:636
msgid "Undo"
msgstr "Visszavonás"
#: src/widgets/image_window.rs:685
#: src/widgets/image_window.rs:666
msgid "Failed to move image to trash"
msgstr "Nem sikerült áthelyezni a képet a kukába"
#: src/widgets/image_window.rs:709
#: src/widgets/image_window.rs:690
msgid "Permanently Delete Image?"
msgstr "Véglegesen törli a képet?"
#: src/widgets/image_window.rs:711
#: src/widgets/image_window.rs:692
#, rust-format
msgid "After deleting the image “{}” it will be permanently lost."
msgstr "A(z) „{}” kép a törlése után véglegesen elvész."
#: src/widgets/image_window.rs:719
msgid "Cancel"
msgstr "Mégse"
#: src/widgets/image_window.rs:720
#: src/widgets/image_window.rs:701
msgid "Delete"
msgstr "Törlés"
#: src/widgets/image_window.rs:731
#: src/widgets/image_window.rs:712
msgid "Failed to delete image"
msgstr "Nem sikerült törölni a képet"
@ -730,7 +823,6 @@ msgid "Edit Image"
msgstr "Kép szerkesztése"
#: src/widgets/image_window.ui:220
#| msgid "View Images"
msgid "View and Edit Images"
msgstr "Képek megtekintése és szerkesztése"
@ -778,23 +870,24 @@ msgid "px"
msgstr "képpont"
#. Translators: {} is a placeholder for the filename
#: src/widgets/print.rs:364
#: src/widgets/print.rs:365
#, rust-format
msgid "Print “{}”"
msgstr "„{}” nyomtatása"
#: src/widgets/print.rs:477
#: src/widgets/print.rs:472
msgid "Continue…"
msgstr "Folytatás…"
#: src/widgets/print.rs:478
#: src/widgets/print.rs:473
msgid "Print Image"
msgstr "Kép nyomtatása"
#: src/widgets/print.rs:797
#: src/widgets/print.rs:801
msgid "_Scale"
msgstr "_Méretezés"
#: src/widgets/print.rs:800
#: src/widgets/print.rs:804
msgid "_Width"
msgstr "_Szélesség"
@ -859,30 +952,75 @@ msgstr "Hely _kitöltése"
msgid "H_eight"
msgstr "_Magasság"
#: src/widgets/properties_view.rs:258
#: src/widgets/properties_view.rs:284
msgid "Unknown"
msgstr "Ismeretlen"
#. Translators: Addition of image being transparent to format name
#: src/widgets/properties_view.rs:262
#: src/widgets/properties_view.rs:288
msgid ", transparent"
msgstr ", átlátszó"
#. Translators: Addition of image being grayscale to format name
#: src/widgets/properties_view.rs:267
#: src/widgets/properties_view.rs:293
msgid ", grayscale"
msgstr ", szürkeárnyalatos"
#. Translators: Addition of bit size to format name
#: src/widgets/properties_view.rs:272
#: src/widgets/properties_view.rs:298
msgid ", {}\\u{202F}bit"
msgstr ", {}\\u{202F} bites"
#: src/widgets/properties_view.rs:290
#: src/widgets/properties_view.rs:315
msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgstr "{}\\u{202F}\\u{D7}\\u{202F}{}"
#: src/widgets/properties_view.rs:326
#. Translators: Shorthand for centimeter
#: src/widgets/properties_view.rs:354
msgid " cm"
msgstr " cm"
#. Translators: Shorthand for inch
#: src/widgets/properties_view.rs:358
msgid "″"
msgstr "″"
#. Translators: Shorthand for meter
#: src/widgets/properties_view.rs:362
msgid " m"
msgstr " m"
#. Translators: Shorthand for millimeter
#: src/widgets/properties_view.rs:366
msgid " mm"
msgstr " mm"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:370
msgid " pc"
msgstr " pc"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:374
msgid " pt"
msgstr " pt"
#: src/widgets/properties_view.rs:386
#| msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgid "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
msgstr "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
#: src/widgets/properties_view.rs:403
#| msgid "{}\\u{202F}s"
msgid "{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:407
#| msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgid "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:435
msgid "Image Coordinates Copied"
msgstr "Képkoordináták másolva"
@ -899,62 +1037,95 @@ msgid "URI"
msgstr "URI"
#: src/widgets/properties_view.ui:66
#| msgid "File Size"
msgid "Physical Size"
msgstr "Fizikai méret"
#: src/widgets/properties_view.ui:76
msgid "Resolution"
msgstr "Felbontás"
#: src/widgets/properties_view.ui:86
msgid "Image Format"
msgstr "Kép formátuma"
#: src/widgets/properties_view.ui:76
#: src/widgets/properties_view.ui:96
msgid "File Size"
msgstr "Fájl mérete"
#: src/widgets/properties_view.ui:90
#: src/widgets/properties_view.ui:110
msgid "File Created"
msgstr "Fájl létrehozva"
#: src/widgets/properties_view.ui:100
#: src/widgets/properties_view.ui:120
msgid "File Modified"
msgstr "Fájl módosítva"
#. Translators: This is a label for GPS coordinates
#: src/widgets/properties_view.ui:114
#: src/widgets/properties_view.ui:134
msgid "Location"
msgstr "Hely"
#: src/widgets/properties_view.ui:125
#: src/widgets/properties_view.ui:145
msgid "Copy Coordinates"
msgstr "Koordináták másolása"
#: src/widgets/properties_view.ui:136
#: src/widgets/properties_view.ui:156
msgid "Open Location"
msgstr "Hely megnyitása"
#: src/widgets/properties_view.ui:149
#: src/widgets/properties_view.ui:169
msgid "Originally Created"
msgstr "Eredetileg létrehozva"
#: src/widgets/properties_view.ui:159
#: src/widgets/properties_view.ui:179
msgid "Creator"
msgstr "Létrehozó"
#: src/widgets/properties_view.ui:189
msgid "Copyright"
msgstr "Szerzői jog"
#: src/widgets/properties_view.ui:199
msgid "Comment"
msgstr "Megjegyzés"
#: src/widgets/properties_view.ui:169
#: src/widgets/properties_view.ui:209
msgid "Aperture"
msgstr "Rekesz"
#: src/widgets/properties_view.ui:179
#: src/widgets/properties_view.ui:219
msgid "Exposure"
msgstr "Expozíció"
#: src/widgets/properties_view.ui:189
#: src/widgets/properties_view.ui:229
msgid "ISO"
msgstr "ISO"
#: src/widgets/properties_view.ui:199
#: src/widgets/properties_view.ui:239
msgid "Focal Length"
msgstr "Gyújtótávolság"
#: src/widgets/properties_view.ui:209
#: src/widgets/properties_view.ui:249
msgid "Maker, Model"
msgstr "Gyártó, modell"
#: src/widgets/window.rs:206
#: src/widgets/properties_view.ui:259
msgid "Lens"
msgstr "Lencsék"
#: src/widgets/properties_view.ui:269
msgid "Software"
msgstr "Szoftver"
#: src/widgets/properties_view.ui:279
msgid "ICC Profile"
msgstr "ICC-profil"
#: src/widgets/properties_view.ui:289
msgid "CICP"
msgstr "CICP"
#: src/widgets/window.rs:254
msgid "Show Details"
msgstr "Részletek megjelenítése"

View file

@ -1,23 +1,29 @@
# Norwegian Bokmål translation for loupe.
# Copyright (C) 2023 loupe's COPYRIGHT HOLDER
# This file is distributed under the same license as the loupe package.
# Brage Fuglseth <bragefuglseth@gnome.org>, 2023.
# Kjartan Maraas <kmaraas@gnome.org>, 2026
# Brage Fuglseth <bragefuglseth@gnome.org>, 2023-2026.
#
msgid ""
msgstr ""
"Project-Id-Version: loupe main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/loupe/-/issues\n"
"POT-Creation-Date: 2026-08-06 11:47+0000\n"
"PO-Revision-Date: 2026-08-24 20:45+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian Bokmål <i18n-nb@lister.ping.uio.no>\n"
"POT-Creation-Date: 2026-09-01 12:44+0000\n"
"PO-Revision-Date: 2026-09-03 23:18+0200\n"
"Last-Translator: Brage Fuglseth <bragefuglseth@gnome.org>\n"
"Language-Team: Norwegian Bokmål\n"
"Language: nb\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.9\n"
"X-Generator: Gtranslator 50.0\n"
"X-DL-VCS-Web: https://gitlab.gnome.org/GNOME/loupe\n"
"X-DL-Lang: nb\n"
"X-DL-Module: loupe\n"
"X-DL-Branch: main\n"
"X-DL-Domain: po\n"
"X-DL-State: None\n"
#: data/org.gnome.Loupe.desktop.in.in:2
#: data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
@ -304,7 +310,7 @@ msgstr "\\u{192}\\u{2215}{}"
#. Translators: Fractional exposure time (photography) in seconds
#: src/metadata.rs:283
msgid "1\\u{2215}{}\\u{202F}s"
msgstr "1\\u{2215}{}u{202F} s"
msgstr "1\\u{2215}{}u\\{202F}s"
#. Translators: Exposure time (photography) in seconds
#: src/metadata.rs:291
@ -341,11 +347,11 @@ msgstr "DCI P3"
#: src/metadata.rs:493
msgid "Display P3"
msgstr ""
msgstr "Display P3"
#: src/metadata.rs:494
msgid "Rec.2020"
msgstr ""
msgstr "Rec.2020"
#: src/metadata.rs:495
msgid "sRGB"
@ -546,7 +552,9 @@ msgstr "Lagre endringer?"
msgid ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
msgstr "Det finnes endringer på bildet som ikke er lagret. Hvis du lukker uten å lagre vil du miste disse endringene."
msgstr ""
"Det finnes endringer på bildet som ikke er lagret. Hvis du lukker uten å "
"lagre vil du miste disse endringene."
#: src/widgets/edit_window.rs:532 src/widgets/image_window.rs:700
msgid "Cancel"

585
po/nn.po

File diff suppressed because it is too large Load diff

View file

@ -4,27 +4,27 @@
# MarcosPatrick <marcosgnr2002@yahoo.com.br>, 2023.
# Leônidas Araújo <leorusvellt@hotmail.com>, 2023-2024.
# Rafael Fontenelle <rafaelff@gnome.org>, 2023-2025.
# Álvaro Burns, 2025.
# Juliano de Souza Camargo <julianosc@pm.me>, 2024-2026.
# Álvaro Burns <>, 2025-2026.
#
msgid ""
msgstr ""
"Project-Id-Version: Loupe main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/loupe/-/issues\n"
"POT-Creation-Date: 2026-01-30 22:01+0000\n"
"PO-Revision-Date: 2026-01-31 19:03-0300\n"
"Last-Translator: Juliano de Souza Camargo <julianosc@pm.me>\n"
"POT-Creation-Date: 2026-08-06 11:47+0000\n"
"PO-Revision-Date: 2026-08-15 09:35-0300\n"
"Last-Translator: Álvaro Burns <>\n"
"Language-Team: Brazilian Portuguese <https://br.gnome.org/traducao/>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Gtranslator 49.0\n"
"X-Generator: Gtranslator 50.0\n"
#: data/org.gnome.Loupe.desktop.in.in:2
#: data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
#: src/widgets/image_window.rs:897
#: src/widgets/image_window.rs:879
msgid "Image Viewer"
msgstr "Visualizador de imagens"
@ -42,7 +42,8 @@ msgid "Show the image properties sidebar"
msgstr "Mostrar a barra lateral de propriedades da imagem"
#. developer_name tag deprecated with Appstream 1.0
#: data/org.gnome.Loupe.metainfo.xml.in.in:8 src/about.rs:35
#: data/org.gnome.Loupe.metainfo.xml.in.in:8
#: data/org.gnome.Loupe.metainfo.xml.in.in:10 src/about.rs:35
msgid "The GNOME Project"
msgstr "O Projeto GNOME"
@ -54,6 +55,18 @@ msgstr ""
"Explore imagens, veja seus metadados, e faça edições rápidas como recortar, "
"rotacionar e espelhá-las."
#: data/org.gnome.Loupe.metainfo.xml.in.in:28
msgid "Main Window"
msgstr "Janela principal"
#: data/org.gnome.Loupe.metainfo.xml.in.in:32
msgid "Crop Tool"
msgstr "Ferramenta de corte"
#: data/org.gnome.Loupe.metainfo.xml.in.in:36
msgid "Image Properties Panel"
msgstr "Painel de propriedades da imagem"
#: data/resources/shortcuts-dialog.ui:6
msgctxt "shortcut window"
msgid "General Shortcuts"
@ -291,44 +304,85 @@ msgid "Could not list other files in directory."
msgstr "Não foi possível listar outros arquivos no diretório."
#. Translators: Display of the f-number <https://en.wikipedia.org/wiki/F-number>. {} will be replaced with the number
#: src/metadata.rs:256
#: src/metadata.rs:270
msgid "\\u{192}\\u{2215}{}"
msgstr "\\u{192}\\u{2215}{}"
#. Translators: Fractional exposure time (photography) in seconds
#: src/metadata.rs:269
#: src/metadata.rs:283
msgid "1\\u{2215}{}\\u{202F}s"
msgstr "1\\u{2215}{}\\u{202F}s"
#. Translators: Exposure time (photography) in seconds
#: src/metadata.rs:277
#: src/metadata.rs:291
msgid "{}\\u{202F}s"
msgstr "{}\\u{202F}s"
#: src/metadata.rs:291
#: src/metadata.rs:305
msgid "{}\\u{202F}mm"
msgstr "{}\\u{202F}mm"
#. Translators: short for "north" in GPS coordinate
#: src/metadata.rs:331
#: src/metadata.rs:395
msgid "N"
msgstr "N"
#. Translators: short for "south" in GPS coordinate
#: src/metadata.rs:335
#: src/metadata.rs:399
msgid "S"
msgstr "S"
#. Translators: short for "east" in GPS coordinate
#: src/metadata.rs:344
#: src/metadata.rs:408
msgid "E"
msgstr "L"
#. Translators: short for "west" in GPS coordinate
#: src/metadata.rs:348
#: src/metadata.rs:412
msgid "W"
msgstr "O"
#: src/metadata.rs:492
msgid "DCI P3"
msgstr "DCI P3"
#: src/metadata.rs:493
msgid "Display P3"
msgstr "Display P3"
#: src/metadata.rs:494
msgid "Rec.2020"
msgstr "Rec.2020"
#: src/metadata.rs:495
msgid "sRGB"
msgstr "sRGB"
#: src/metadata.rs:496 src/metadata.rs:504
msgid "Unspecified"
msgstr "Não especificado"
#: src/metadata.rs:497 src/metadata.rs:506 src/metadata.rs:516
#, rust-format
msgid "Unknown ({})"
msgstr "Desconhecido ({})"
#: src/metadata.rs:503
msgid "ICtCp"
msgstr "ICtCp"
#: src/metadata.rs:512
msgid "DCI"
msgstr "DCI"
#: src/metadata.rs:513
msgid "PQ"
msgstr "PQ"
#: src/metadata.rs:514
msgid "Gamma 2.4"
msgstr "Gamma 2.4"
#. / Returns localized date + time format
#. Translators: This is the date and time format we use in metadata output etc.
#. The format has to follow <https://docs.gtk.org/glib/method.DateTime.format.html>
@ -456,32 +510,64 @@ msgid "Crop Selection"
msgstr "Corta a seleção"
#. Translators: Filename suffix, {} is replaced by a number
#: src/widgets/edit_window.rs:195
#: src/widgets/edit_window.rs:207
#, rust-format
msgid " (Edited)"
msgid_plural " (Edited {})"
msgstr[0] "(Editado)"
msgstr[1] "(Editados {})"
#: src/widgets/edit_window.rs:289 src/widgets/edit_window.rs:315
#: src/widgets/edit_window.rs:292
msgid "Moving Original to Trash"
msgstr "Movendo original para a lixeira"
#: src/widgets/edit_window.rs:306 src/widgets/edit_window.rs:335
msgid "Failed to save image."
msgstr "Falha ao salvar a imagem."
#: src/widgets/edit_window.rs:343
#: src/widgets/edit_window.rs:315
msgid "Finalizing Save Operation"
msgstr "Finalizando operação de salvamento"
#: src/widgets/edit_window.rs:367
msgid "Editing Image"
msgstr "Editando imagem"
#: src/widgets/edit_window.rs:360
#: src/widgets/edit_window.rs:384
msgid "Failed to edit image."
msgstr "Falha ao editar a imagem."
#: src/widgets/edit_window.rs:370 src/widgets/edit_window.rs:393
#: src/widgets/edit_window.rs:391
msgid "Saving Image"
msgstr "Salvando imagem"
#: src/widgets/edit_window.rs:410
msgid "Failed to Save Image"
msgstr "Falha ao salvar a imagem"
#: src/widgets/edit_window.rs:376
msgid "Saving Image"
msgstr "Salvando imagem"
#: src/widgets/edit_window.rs:526
msgid "Save Changes?"
msgstr "Salvar mudanças?"
#: src/widgets/edit_window.rs:528
msgid ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
msgstr ""
"Há mudanças na imagem que não foram salvas. Se fechar sem salvar, perderá estas "
"mudanças permanentemente."
#: src/widgets/edit_window.rs:532 src/widgets/image_window.rs:700
msgid "Cancel"
msgstr "Cancelar"
#: src/widgets/edit_window.rs:533
msgid "Discard"
msgstr "Descartar"
#: src/widgets/edit_window.rs:534
msgid "Save"
msgstr "Salvar"
#: src/widgets/edit_window.ui:64
msgid "_Cancel"
@ -616,11 +702,11 @@ msgstr "Gira à direita"
#. Translators: This is a toast notification, informing the user that
#. an image has been set as background.
#: src/widgets/image_view.rs:866
#: src/widgets/image_view.rs:870
msgid "Set as background."
msgstr "Definida como plano de fundo."
#: src/widgets/image_view.rs:872
#: src/widgets/image_view.rs:876
msgid "Could not set background."
msgstr "Não foi possível definir o plano de fundo."
@ -656,52 +742,48 @@ msgstr "Nível de ampliação customizado"
msgid "Zoom to Specified Level"
msgstr "Amplia para nível específico"
#: src/widgets/image_window.rs:527
#: src/widgets/image_window.rs:509
msgid "Supported image formats"
msgstr "Formatos de imagem suportados"
#: src/widgets/image_window.rs:533
#: src/widgets/image_window.rs:515
msgid "All files"
msgstr "Todos arquivos"
#: src/widgets/image_window.rs:540
#: src/widgets/image_window.rs:522
msgid "Open Image"
msgstr "Abrir imagem"
#: src/widgets/image_window.rs:632
#: src/widgets/image_window.rs:614
msgid "Image copied to clipboard"
msgstr "Imagem copiada para a área de transferência"
#: src/widgets/image_window.rs:653
#: src/widgets/image_window.rs:635
msgid "Image moved to trash"
msgstr "Imagem movida para a lixeira"
#: src/widgets/image_window.rs:654
#: src/widgets/image_window.rs:636
msgid "Undo"
msgstr "Desfazer"
#: src/widgets/image_window.rs:684
#: src/widgets/image_window.rs:666
msgid "Failed to move image to trash"
msgstr "Falha ao mover a imagem para a lixeira"
#: src/widgets/image_window.rs:708
#: src/widgets/image_window.rs:690
msgid "Permanently Delete Image?"
msgstr "Excluir imagem permanentemente?"
#: src/widgets/image_window.rs:710
#: src/widgets/image_window.rs:692
#, rust-format
msgid "After deleting the image “{}” it will be permanently lost."
msgstr "Após excluir a imagem “{}” ela será perdida permanentemente."
#: src/widgets/image_window.rs:718
msgid "Cancel"
msgstr "Cancelar"
#: src/widgets/image_window.rs:719
#: src/widgets/image_window.rs:701
msgid "Delete"
msgstr "Excluir"
#: src/widgets/image_window.rs:730
#: src/widgets/image_window.rs:712
msgid "Failed to delete image"
msgstr "Falha ao excluir a imagem"
@ -789,24 +871,24 @@ msgid "px"
msgstr "px"
#. Translators: {} is a placeholder for the filename
#: src/widgets/print.rs:364
#: src/widgets/print.rs:365
#, rust-format
msgid "Print “{}”"
msgstr "Imprimir “{}”"
#: src/widgets/print.rs:477
#: src/widgets/print.rs:472
msgid "Continue…"
msgstr "Continuar…"
#: src/widgets/print.rs:478
#: src/widgets/print.rs:473
msgid "Print Image"
msgstr "Imprimir imagem"
#: src/widgets/print.rs:797
#: src/widgets/print.rs:801
msgid "_Scale"
msgstr "E_scala"
#: src/widgets/print.rs:800
#: src/widgets/print.rs:804
msgid "_Width"
msgstr "_Largura"
@ -871,30 +953,72 @@ msgstr "P_reencher espaço"
msgid "H_eight"
msgstr "A_ltura"
#: src/widgets/properties_view.rs:258
#: src/widgets/properties_view.rs:284
msgid "Unknown"
msgstr "Desconhecido"
#. Translators: Addition of image being transparent to format name
#: src/widgets/properties_view.rs:262
#: src/widgets/properties_view.rs:288
msgid ", transparent"
msgstr ", transparente"
#. Translators: Addition of image being grayscale to format name
#: src/widgets/properties_view.rs:267
#: src/widgets/properties_view.rs:293
msgid ", grayscale"
msgstr ", tons de cinza"
#. Translators: Addition of bit size to format name
#: src/widgets/properties_view.rs:272
#: src/widgets/properties_view.rs:298
msgid ", {}\\u{202F}bit"
msgstr ", {}\\u{202F}bit"
#: src/widgets/properties_view.rs:290
#: src/widgets/properties_view.rs:315
msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgstr "{}\\u{202F}\\u{D7}\\u{202F}{}"
#: src/widgets/properties_view.rs:326
#. Translators: Shorthand for centimeter
#: src/widgets/properties_view.rs:354
msgid " cm"
msgstr " cm"
#. Translators: Shorthand for inch
#: src/widgets/properties_view.rs:358
msgid "″"
msgstr "″"
#. Translators: Shorthand for meter
#: src/widgets/properties_view.rs:362
msgid " m"
msgstr " m"
#. Translators: Shorthand for millimeter
#: src/widgets/properties_view.rs:366
msgid " mm"
msgstr " mm"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:370
msgid " pc"
msgstr " pc"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:374
msgid " pt"
msgstr " pt"
#: src/widgets/properties_view.rs:386
msgid "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
msgstr "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
#: src/widgets/properties_view.rs:403
msgid "{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:407
msgid "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
msgstr "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
#: src/widgets/properties_view.rs:435
msgid "Image Coordinates Copied"
msgstr "Coordenadas de imagem copiadas"
@ -911,63 +1035,95 @@ msgid "URI"
msgstr "URI"
#: src/widgets/properties_view.ui:66
msgid "Physical Size"
msgstr "Tamanho físico"
#: src/widgets/properties_view.ui:76
msgid "Resolution"
msgstr "Resolução"
#: src/widgets/properties_view.ui:86
msgid "Image Format"
msgstr "Formato de imagem"
#: src/widgets/properties_view.ui:76
#: src/widgets/properties_view.ui:96
msgid "File Size"
msgstr "Tamanho do arquivo"
#: src/widgets/properties_view.ui:90
#: src/widgets/properties_view.ui:110
msgid "File Created"
msgstr "Arquivo criado"
#: src/widgets/properties_view.ui:100
#: src/widgets/properties_view.ui:120
msgid "File Modified"
msgstr "Arquivo modificado"
#. Translators: This is a label for GPS coordinates
#: src/widgets/properties_view.ui:114
#: src/widgets/properties_view.ui:134
msgid "Location"
msgstr "Local"
#: src/widgets/properties_view.ui:125
#: src/widgets/properties_view.ui:145
msgid "Copy Coordinates"
msgstr "Copia coordenadas"
#: src/widgets/properties_view.ui:136
#: src/widgets/properties_view.ui:156
msgid "Open Location"
msgstr "Abre o local"
#: src/widgets/properties_view.ui:149
#: src/widgets/properties_view.ui:169
msgid "Originally Created"
msgstr "Originalmente criado"
#: src/widgets/properties_view.ui:159
#: src/widgets/properties_view.ui:179
msgid "Creator"
msgstr "Criador"
#: src/widgets/properties_view.ui:189
msgid "Copyright"
msgstr "Copyright"
#: src/widgets/properties_view.ui:199
msgid "Comment"
msgstr "Comentário"
#: src/widgets/properties_view.ui:169
#: src/widgets/properties_view.ui:209
msgid "Aperture"
msgstr "Abertura"
#: src/widgets/properties_view.ui:179
#: src/widgets/properties_view.ui:219
msgid "Exposure"
msgstr "Exposição"
#: src/widgets/properties_view.ui:189
#: src/widgets/properties_view.ui:229
msgid "ISO"
msgstr "ISO"
#: src/widgets/properties_view.ui:199
#: src/widgets/properties_view.ui:239
msgid "Focal Length"
msgstr "Comprimento focal"
#: src/widgets/properties_view.ui:209
#: src/widgets/properties_view.ui:249
msgid "Maker, Model"
msgstr "Fabricante, Modelo"
#: src/widgets/window.rs:206
#: src/widgets/properties_view.ui:259
msgid "Lens"
msgstr "Lentes"
#: src/widgets/properties_view.ui:269
msgid "Software"
msgstr "Programa"
#: src/widgets/properties_view.ui:279
msgid "ICC Profile"
msgstr "Perfil ICC"
#: src/widgets/properties_view.ui:289
msgid "CICP"
msgstr "CICP"
#: src/widgets/window.rs:254
msgid "Show Details"
msgstr "Mostra detalhes"
@ -990,9 +1146,6 @@ msgstr "Mostra detalhes"
#~ msgid "Gestures"
#~ msgstr "Gestos"
#~ msgid "{} %"
#~ msgstr "{} %"
#~ msgid "Copy to Clipboard"
#~ msgstr "Copiar para área de transferência"

417
po/sr.po
View file

@ -8,24 +8,26 @@ msgid ""
msgstr ""
"Project-Id-Version: loupe main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/loupe/-/issues\n"
"POT-Creation-Date: 2026-03-13 14:05+0000\n"
"PO-Revision-Date: 2026-03-17 14:57+0100\n"
"Last-Translator: Марко М. Костић <marko.m.kostic@gmail.com>\n"
"POT-Creation-Date: 2026-08-26 16:17+0000\n"
"PO-Revision-Date: 2026-08-30 11:44+0200\n"
"Last-Translator: Марко Костић <marko.m.kostic@gmail.com>\n"
"Language-Team: Serbian <Serbian <gnome-sr@googlegroups.com>>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || "
"n%100>=20) ? 1 : 2;\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Poedit 3.9\n"
#: data/org.gnome.Loupe.desktop.in.in:2 data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
#: src/widgets/image_window.rs:898
#: data/org.gnome.Loupe.desktop.in.in:2
#: data/org.gnome.Loupe.metainfo.xml.in.in:6 src/about.rs:32
#: src/widgets/image_window.rs:879
msgid "Image Viewer"
msgstr "Прегледач слика"
#: data/org.gnome.Loupe.desktop.in.in:3 data/org.gnome.Loupe.metainfo.xml.in.in:13
#: data/org.gnome.Loupe.desktop.in.in:3
#: data/org.gnome.Loupe.metainfo.xml.in.in:13
msgid "View and edit images"
msgstr "Прегледајте и уређујте слике"
@ -35,16 +37,31 @@ msgstr "Picture;Graphics;Loupe;Слика;Графика;Лупа;Slika;Grafika;
#: data/org.gnome.Loupe.gschema.xml.in:6
msgid "Show the image properties sidebar"
msgstr "Прикажи бочну површ са својствима слике"
msgstr "Прикажи бочну траку са својствима слике"
#. developer_name tag deprecated with Appstream 1.0
#: data/org.gnome.Loupe.metainfo.xml.in.in:8 src/about.rs:35
#: data/org.gnome.Loupe.metainfo.xml.in.in:8
#: data/org.gnome.Loupe.metainfo.xml.in.in:10 src/about.rs:35
msgid "The GNOME Project"
msgstr "Пројекат Гном"
#: data/org.gnome.Loupe.metainfo.xml.in.in:15
msgid "Explore images, view their metadata, and make quick edits by cropping, rotating, or flipping them."
msgstr "Истражујте слике, прегледајте њихове метаподатке и правите брзе измене опсецањем, окретањем или извртањем."
msgid ""
"Explore images, view their metadata, and make quick edits by cropping, "
"rotating, or flipping them."
msgstr "Истражујте слике, прегледајте њихове метаподатке и правите брзе измене исецањем, окретањем или извртањем."
#: data/org.gnome.Loupe.metainfo.xml.in.in:28
msgid "Main Window"
msgstr "Главни прозор"
#: data/org.gnome.Loupe.metainfo.xml.in.in:32
msgid "Crop Tool"
msgstr "Алат за исецање"
#: data/org.gnome.Loupe.metainfo.xml.in.in:36
msgid "Image Properties Panel"
msgstr "Трака за својства слике"
#: data/resources/shortcuts-dialog.ui:6
msgctxt "shortcut window"
@ -94,7 +111,7 @@ msgstr "Прикажи пречице тастатуре"
#: data/resources/shortcuts-dialog.ui:57
msgctxt "shortcut window"
msgid "Close Window"
msgstr "Затварање прозора"
msgstr "Затвори прозор"
#: data/resources/shortcuts-dialog.ui:63
msgctxt "shortcut window"
@ -131,12 +148,14 @@ msgctxt "shortcut window"
msgid "Rotate Image"
msgstr "Заокретање слике"
#: data/resources/shortcuts-dialog.ui:119 data/resources/shortcuts-dialog.ui:253
#: data/resources/shortcuts-dialog.ui:119
#: data/resources/shortcuts-dialog.ui:253
msgctxt "shortcut window"
msgid "Rotate Clockwise"
msgstr "Окретање на десно"
#: data/resources/shortcuts-dialog.ui:125 data/resources/shortcuts-dialog.ui:259
#: data/resources/shortcuts-dialog.ui:125
#: data/resources/shortcuts-dialog.ui:259
msgctxt "shortcut window"
msgid "Rotate Counterclockwise"
msgstr "Окретање на лево"
@ -144,17 +163,17 @@ msgstr "Окретање на лево"
#: data/resources/shortcuts-dialog.ui:133
msgctxt "shortcut window"
msgid "Zoom Image"
msgstr "Увеличај слику"
msgstr "Увећање слике"
#: data/resources/shortcuts-dialog.ui:136
msgctxt "shortcut window"
msgid "Zoom In"
msgstr "Увећај приказ"
msgstr "Увећај"
#: data/resources/shortcuts-dialog.ui:142
msgctxt "shortcut window"
msgid "Zoom Out"
msgstr "Умањи приказ"
msgstr "Умањи"
#: data/resources/shortcuts-dialog.ui:148
msgctxt "shortcut window"
@ -174,27 +193,27 @@ msgstr "200% размера"
#: data/resources/shortcuts-dialog.ui:168
msgctxt "shortcut window"
msgid "Pan View"
msgstr "Панирај поглед"
msgstr "Помери поглед"
#: data/resources/shortcuts-dialog.ui:171
msgctxt "shortcut window"
msgid "Pan Left"
msgstr "Панирај на лево"
msgstr "Помери на лево"
#: data/resources/shortcuts-dialog.ui:177
msgctxt "shortcut window"
msgid "Pan Right"
msgstr "Панирај на десно"
msgstr "Помери на десно"
#: data/resources/shortcuts-dialog.ui:183
msgctxt "shortcut window"
msgid "Pan Up"
msgstr "Панирај нагоре"
msgstr "Помери нагоре"
#: data/resources/shortcuts-dialog.ui:189
msgctxt "shortcut window"
msgid "Pan Down"
msgstr "Панирај надоле"
msgstr "Помери надоле"
#: data/resources/shortcuts-dialog.ui:197
msgctxt "shortcut window"
@ -270,51 +289,92 @@ msgstr ""
#: src/about.rs:46
msgid "Copyright © 2020–2024 Christopher Davis et al."
msgstr "Ауторско право © 2020–2024 Кристофер Дејвис (Christopher Davis) и сарадници"
msgstr "Ауторско право © 2020–2024 Кристофер Дејвис (Christopher Davis) и сарадници."
#: src/file_model.rs:211
msgid "Could not list other files in directory."
msgstr "Не могу да излистим остале датотеке у директоријуму."
#. Translators: Display of the f-number <https://en.wikipedia.org/wiki/F-number>. {} will be replaced with the number
#: src/metadata.rs:256
#: src/metadata.rs:270
msgid "\\u{192}\\u{2215}{}"
msgstr "\\u{192}\\u{2215}{}"
#. Translators: Fractional exposure time (photography) in seconds
#: src/metadata.rs:269
#: src/metadata.rs:283
msgid "1\\u{2215}{}\\u{202F}s"
msgstr "1\\u{2215}{}\\u{202F}с"
#. Translators: Exposure time (photography) in seconds
#: src/metadata.rs:277
#: src/metadata.rs:291
msgid "{}\\u{202F}s"
msgstr "{}\\u{202F}с"
#: src/metadata.rs:291
#: src/metadata.rs:305
msgid "{}\\u{202F}mm"
msgstr "{}\\u{202F}мм"
#. Translators: short for "north" in GPS coordinate
#: src/metadata.rs:331
#: src/metadata.rs:395
msgid "N"
msgstr "С"
#. Translators: short for "south" in GPS coordinate
#: src/metadata.rs:335
#: src/metadata.rs:399
msgid "S"
msgstr "Ј"
#. Translators: short for "east" in GPS coordinate
#: src/metadata.rs:344
#: src/metadata.rs:408
msgid "E"
msgstr "И"
#. Translators: short for "west" in GPS coordinate
#: src/metadata.rs:348
#: src/metadata.rs:412
msgid "W"
msgstr "З"
#: src/metadata.rs:492
msgid "DCI P3"
msgstr "DCI P3"
#: src/metadata.rs:493
msgid "Display P3"
msgstr "Display P3"
#: src/metadata.rs:494
msgid "Rec.2020"
msgstr "Rec.2020"
#: src/metadata.rs:495
msgid "sRGB"
msgstr "sRGB"
#: src/metadata.rs:496 src/metadata.rs:504
msgid "Unspecified"
msgstr "Неодређено"
#: src/metadata.rs:497 src/metadata.rs:506 src/metadata.rs:516
#, rust-format
msgid "Unknown ({})"
msgstr "Непознато ({})"
#: src/metadata.rs:503
msgid "ICtCp"
msgstr "ICtCp"
#: src/metadata.rs:512
msgid "DCI"
msgstr "DCI"
#: src/metadata.rs:513
msgid "PQ"
msgstr "PQ"
#: src/metadata.rs:514
msgid "Gamma 2.4"
msgstr "Гама 2.4"
#. / Returns localized date + time format
#. Translators: This is the date and time format we use in metadata output etc.
#. The format has to follow <https://docs.gtk.org/glib/method.DateTime.format.html>
@ -330,7 +390,7 @@ msgstr "Нисам успео да вратим слику из смећа"
#: src/widgets/drag_overlay.ui:15
msgid "Drop Image to View"
msgstr "Превуците слику да је прегледате"
msgstr "Превуци слику за преглед"
#: src/widgets/edit/crop.rs:363
msgid "Failed to Edit Image"
@ -404,9 +464,10 @@ msgstr "Положена слика"
msgid "Orientation Portrait"
msgstr "Усправна слика"
#: src/widgets/edit/crop.ui:252 src/widgets/image_page.ui:15 src/widgets/image_window.ui:25
#: src/widgets/edit/crop.ui:252 src/widgets/image_page.ui:15
#: src/widgets/image_window.ui:25
msgid "Rotate"
msgstr "Окрени"
msgstr "Заокрени"
#: src/widgets/edit/crop.ui:261 src/widgets/edit/crop.ui:407
msgid "Rotate Counter Clockwise"
@ -434,42 +495,72 @@ msgstr "В_рати"
#: src/widgets/edit/crop.ui:307 src/widgets/edit/crop.ui:431
msgid "Undo Changes in this View"
msgstr "Поништи промене у овом прегледу"
msgstr "Опозови измене у овом прегледу"
#: src/widgets/edit/crop_selection.ui:46
msgid "Crop Selection"
msgstr "Исеци изабрано"
#. Translators: Filename suffix, {} is replaced by a number
#: src/widgets/edit_window.rs:196
#: src/widgets/edit_window.rs:207
#, rust-format
msgid " (Edited)"
msgid_plural " (Edited {})"
msgstr[0] " ({} уређена)"
msgstr[1] " ({} уређене)"
msgstr[2] " ({} уређених)"
msgstr[3] " (уређена)"
msgstr[0] " (измењено {})"
msgstr[1] " (измењено {})"
msgstr[2] " (измењено {})"
msgstr[3] " (измењено)"
#: src/widgets/edit_window.rs:290 src/widgets/edit_window.rs:316
#: src/widgets/edit_window.rs:292
msgid "Moving Original to Trash"
msgstr "Премештам оригинал у смеће"
#: src/widgets/edit_window.rs:306 src/widgets/edit_window.rs:335
msgid "Failed to save image."
msgstr "Не могу да сачувам слику."
msgstr "Грешка при чувању слике."
#: src/widgets/edit_window.rs:344
#: src/widgets/edit_window.rs:315
msgid "Finalizing Save Operation"
msgstr "Завршавам радњу чувања"
#: src/widgets/edit_window.rs:367
msgid "Editing Image"
msgstr "Уређивање слике"
#: src/widgets/edit_window.rs:361
#: src/widgets/edit_window.rs:384
msgid "Failed to edit image."
msgstr "Не могу да уредим слику."
msgstr "Грешка при уређивању слике."
#: src/widgets/edit_window.rs:371 src/widgets/edit_window.rs:394
msgid "Failed to Save Image"
msgstr "Не могу да сачувам слику"
#: src/widgets/edit_window.rs:377
#: src/widgets/edit_window.rs:391
msgid "Saving Image"
msgstr "Чувам слику"
#: src/widgets/edit_window.rs:410
msgid "Failed to Save Image"
msgstr "Грешка при чувању слике"
#: src/widgets/edit_window.rs:526
msgid "Save Changes?"
msgstr "Сачувати измене?"
#: src/widgets/edit_window.rs:528
msgid ""
"There are unsaved image editing changes. Closing without saving will "
"permanently lose these modifications."
msgstr "Постоје несачуване измене уређивања слике. Затварањем без чувања ове измене ће бити трајно изгубљене."
#: src/widgets/edit_window.rs:532 src/widgets/image_window.rs:700
msgid "Cancel"
msgstr "Откажи"
#: src/widgets/edit_window.rs:533
msgid "Discard"
msgstr "Одбаци"
#: src/widgets/edit_window.rs:534
msgid "Save"
msgstr "Сачувај"
#: src/widgets/edit_window.ui:64
msgid "_Cancel"
msgstr "_Откажи"
@ -484,7 +575,7 @@ msgstr "Сачувај _као…"
#: src/widgets/edit_window.ui:100
msgid "Keep original and create a new file"
msgstr "Сачувај оригинал и направи нову датотеку"
msgstr "Задржи оригинал и направи нову датотеку"
#: src/widgets/edit_window.ui:120
msgid "_Replace Original"
@ -492,14 +583,15 @@ msgstr "_Замени оригинал"
#: src/widgets/edit_window.ui:128
msgid "Original image will be moved to the trash"
msgstr "Оригинална слика ће бити премештена у смеће"
msgstr "Изворна слика ће бити премештена у смеће"
#: src/widgets/error_details.rs:80
msgid ""
"The following error occurred. If you think this is an issue with the program, please include this information "
"when you report the error."
"The following error occurred. If you think this is an issue with the "
"program, please include this information when you report the error."
msgstr ""
"Дошло је до грешке. Уколико мислите да је проблем у програму, укључите ове податке када шаљете извештај о грешци."
"Дошло је до грешке. Уколико мислите да је проблем у програму, укључите ове "
"податке када шаљете извештај о грешци."
#: src/widgets/error_details.ui:4
msgid "Error Details"
@ -507,11 +599,13 @@ msgstr "Појединости грешке"
#: src/widgets/error_details.ui:19
msgid ""
"The image loader reported the following when trying to load or edit the image. Please include this information "
"when you report an error. If possible, please also provide the image that causes the issue."
"The image loader reported the following when trying to load or edit the "
"image. Please include this information when you report an error. If "
"possible, please also provide the image that causes the issue."
msgstr ""
"Учитавач слика је јавио следеће приликом учитавања или уређивања слике. Приложите ове податке приликом пријаве "
"грешке. Ако је могуће, закачите и слику која је довела до грешке."
"Учитавач слика је јавио следеће приликом учитавања или уређивања слике. "
"Приложите ове податке приликом пријаве грешке. Ако је могуће, закачите и "
"слику која је довела до грешке."
#: src/widgets/error_details.ui:24
msgid "Error Message"
@ -528,10 +622,9 @@ msgstr "Пр_ијави проблем"
#: src/widgets/image_page.rs:225
#, rust-format
msgid ""
"No image loaders available. Maybe the “glycin-loaders” package with compatibility version “{}” is not installed."
msgstr ""
"Ниједан учитавач слика није доступан. Можда пакет „glycin-loaders“ са издањем компатибилности „{}“ није "
"инсталиран."
"No image loaders available. Maybe the “glycin-loaders” package with "
"compatibility version “{}” is not installed."
msgstr "Ниједан учитавач слика није доступан. Можда пакет „glycin-loaders“ са издањем усаглашености „{}“ није инсталиран."
#: src/widgets/image_page.rs:243
#, rust-format
@ -554,7 +647,7 @@ msgstr "Неуспело учитавање датотеке слике: {}"
#: src/widgets/image_page.rs:272
msgid "Either the image file is corrupted or it contains unsupported elements."
msgstr "Датотека слике је покварена или садржи неподржане елементе."
msgstr "Датотека слике је оштећена или садржи неподржане елементе."
#: src/widgets/image_page.ui:6 src/widgets/image_window.ui:16
msgid "_Open With…"
@ -586,7 +679,7 @@ msgstr "В_ише података"
#: src/widgets/image_page.ui:81
msgid "_Reload"
msgstr "_Освежи"
msgstr "Поно_во учитај"
#: src/widgets/image_page.ui:123 src/widgets/image_window.ui:101
msgid "Rotate Left"
@ -598,11 +691,11 @@ msgstr "Окрени на десно"
#. Translators: This is a toast notification, informing the user that
#. an image has been set as background.
#: src/widgets/image_view.rs:866
#: src/widgets/image_view.rs:870
msgid "Set as background."
msgstr "Постављено за позадину."
#: src/widgets/image_view.rs:872
#: src/widgets/image_view.rs:876
msgid "Could not set background."
msgstr "Не могу поставити позадину."
@ -616,11 +709,11 @@ msgstr "Следећа слика"
#: src/widgets/image_view.ui:66
msgid "Zoom Out"
msgstr "Умањи приказ"
msgstr "Умањи"
#: src/widgets/image_view.ui:77
msgid "Zoom In"
msgstr "Увећај приказ"
msgstr "Увећај"
#: src/widgets/image_view.ui:92
msgid "Toggle Zoom"
@ -638,54 +731,50 @@ msgstr "Произвољни ниво увећања"
msgid "Zoom to Specified Level"
msgstr "Увећај на задати ниво"
#: src/widgets/image_window.rs:528
#: src/widgets/image_window.rs:509
msgid "Supported image formats"
msgstr "Подржане врсте слика"
msgstr "Подржани записи слика"
#: src/widgets/image_window.rs:534
#: src/widgets/image_window.rs:515
msgid "All files"
msgstr "Све датотеке"
#: src/widgets/image_window.rs:541
#: src/widgets/image_window.rs:522
msgid "Open Image"
msgstr "Отвори слику"
#: src/widgets/image_window.rs:633
#: src/widgets/image_window.rs:614
msgid "Image copied to clipboard"
msgstr "Слика је умножена у оставу"
msgstr "Слика је копирана у оставу"
#: src/widgets/image_window.rs:654
#: src/widgets/image_window.rs:635
msgid "Image moved to trash"
msgstr "Слика премештена у смеће"
#: src/widgets/image_window.rs:655
#: src/widgets/image_window.rs:636
msgid "Undo"
msgstr "Опозови"
#: src/widgets/image_window.rs:685
#: src/widgets/image_window.rs:666
msgid "Failed to move image to trash"
msgstr "Премештање слике у смеће није успело"
#: src/widgets/image_window.rs:709
#: src/widgets/image_window.rs:690
msgid "Permanently Delete Image?"
msgstr "Трајно обрисати слику?"
#: src/widgets/image_window.rs:711
#: src/widgets/image_window.rs:692
#, rust-format
msgid "After deleting the image “{}” it will be permanently lost."
msgstr "Након брисања слике „{}“ иста ће бити трајно изгубљена."
#: src/widgets/image_window.rs:719
msgid "Cancel"
msgstr "Откажи"
#: src/widgets/image_window.rs:720
#: src/widgets/image_window.rs:701
msgid "Delete"
msgstr "Обриши"
#: src/widgets/image_window.rs:731
#: src/widgets/image_window.rs:712
msgid "Failed to delete image"
msgstr "Брисање слике није успело"
msgstr "Неуспешно брисање слике"
#: src/widgets/image_window.ui:6
msgid "_New Window"
@ -697,11 +786,11 @@ msgstr "_Отвори…"
#: src/widgets/image_window.ui:46
msgid "_Help"
msgstr "По_моћ"
msgstr "_Помоћ"
#: src/widgets/image_window.ui:50
msgid "_Keyboard Shortcuts"
msgstr "Пречице на _тастатури"
msgstr "Пречице _тастатуре"
#: src/widgets/image_window.ui:54
msgid "_About Image Viewer"
@ -709,7 +798,7 @@ msgstr "_О прегледачу слика"
#: src/widgets/image_window.ui:87
msgid "Toggle Fullscreen"
msgstr "Окини режим преко целог екрана"
msgstr "Окини цео екран"
#: src/widgets/image_window.ui:93
msgid "Main Menu"
@ -737,7 +826,7 @@ msgstr "_Отвори датотеке…"
#: src/widgets/print.rs:109
msgid "Top"
msgstr "Врх"
msgstr "Горе"
#: src/widgets/print.rs:110
msgid "Center"
@ -745,7 +834,7 @@ msgstr "Средина"
#: src/widgets/print.rs:111
msgid "Bottom"
msgstr "Дно"
msgstr "Доле"
#: src/widgets/print.rs:112
msgid "Left"
@ -753,7 +842,7 @@ msgstr "Лево"
#: src/widgets/print.rs:113
msgid "Right"
msgstr "Деснo"
msgstr "Десно"
#. Translators: Shorthand for centimeter
#: src/widgets/print.rs:149
@ -768,29 +857,29 @@ msgstr "инч"
#. Translators: Shorthand for pixel
#: src/widgets/print.rs:153
msgid "px"
msgstr "пиксел"
msgstr "пкс"
#. Translators: {} is a placeholder for the filename
#: src/widgets/print.rs:364
#: src/widgets/print.rs:365
#, rust-format
msgid "Print “{}”"
msgstr "Одштампај „{}“"
msgstr "Штампај „{}“"
#: src/widgets/print.rs:477
#: src/widgets/print.rs:472
msgid "Continue…"
msgstr "Настави…"
#: src/widgets/print.rs:478
#: src/widgets/print.rs:473
msgid "Print Image"
msgstr "Штампај слику"
#: src/widgets/print.rs:797
#: src/widgets/print.rs:801
msgid "_Scale"
msgstr "_Размера"
#: src/widgets/print.rs:800
#: src/widgets/print.rs:804
msgid "_Width"
msgstr "_Ширина"
msgstr "Ш_ирина"
#. Translators: Go back to previous page
#: src/widgets/print.ui:29
@ -823,19 +912,19 @@ msgstr "Пор_авнање"
#: src/widgets/print.ui:108
msgid "Margins"
msgstr "Ивице"
msgstr "Маргине"
#: src/widgets/print.ui:111
msgid "Margin Units"
msgstr "Јединица ивица"
msgstr "Јединице маргина"
#: src/widgets/print.ui:128
msgid "_Horizontal"
msgstr "_Хоризонтално"
msgstr "Водорав_но"
#: src/widgets/print.ui:134
msgid "_Vertical"
msgstr "_Вертикално"
msgstr "Успра_вно"
#: src/widgets/print.ui:142 src/widgets/properties_view.ui:56
msgid "Image Size"
@ -847,36 +936,78 @@ msgstr "Јединица величине слике"
#: src/widgets/print.ui:163
msgid "_Fill Space"
msgstr "Попуни прос_тор"
msgstr "_Попуни простор"
#: src/widgets/print.ui:174
msgid "H_eight"
msgstr "_Висина"
#: src/widgets/properties_view.rs:258
#: src/widgets/properties_view.rs:284
msgid "Unknown"
msgstr "Непознато"
#. Translators: Addition of image being transparent to format name
#: src/widgets/properties_view.rs:262
#: src/widgets/properties_view.rs:288
msgid ", transparent"
msgstr ", провидно"
#. Translators: Addition of image being grayscale to format name
#: src/widgets/properties_view.rs:267
#: src/widgets/properties_view.rs:293
msgid ", grayscale"
msgstr ", сивотоно"
#. Translators: Addition of bit size to format name
#: src/widgets/properties_view.rs:272
#: src/widgets/properties_view.rs:298
msgid ", {}\\u{202F}bit"
msgstr ", {}\\u{202F}бита"
#: src/widgets/properties_view.rs:290
#: src/widgets/properties_view.rs:315
msgid "{}\\u{202F}\\u{D7}\\u{202F}{}"
msgstr "{}\\u{202F}\\u{D7}\\u{202F}{}"
#: src/widgets/properties_view.rs:326
#. Translators: Shorthand for centimeter
#: src/widgets/properties_view.rs:354
msgid " cm"
msgstr " цм"
#. Translators: Shorthand for inch
#: src/widgets/properties_view.rs:358
msgid "″"
msgstr "″"
#. Translators: Shorthand for meter
#: src/widgets/properties_view.rs:362
msgid " m"
msgstr " м"
#. Translators: Shorthand for millimeter
#: src/widgets/properties_view.rs:366
msgid " mm"
msgstr " мм"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:370
msgid " pc"
msgstr " pc"
#. Translators: Shorthand for pica (unit)
#: src/widgets/properties_view.rs:374
msgid " pt"
msgstr " тчк"
#: src/widgets/properties_view.rs:386
msgid "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
msgstr "{}{}\\u{202F}\\u{D7}\\u{202F}{}{}"
#: src/widgets/properties_view.rs:403
msgid "{}\\u{202F}DPI"
msgstr "{}\\u{202F}тпи"
#: src/widgets/properties_view.rs:407
msgid "{}\\u{202F}DPI\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}DPI"
msgstr "{}\\u{202F}тпи\\u{202F}\\u{D7}\\u{202F}{}\\u{202F}тпи"
#: src/widgets/properties_view.rs:435
msgid "Image Coordinates Copied"
msgstr "Координате слике копиране"
@ -893,62 +1024,94 @@ msgid "URI"
msgstr "Адреса"
#: src/widgets/properties_view.ui:66
msgid "Physical Size"
msgstr "Физичка величина"
#: src/widgets/properties_view.ui:76
msgid "Resolution"
msgstr "Резолуција"
#: src/widgets/properties_view.ui:86
msgid "Image Format"
msgstr "Врста слике"
#: src/widgets/properties_view.ui:76
#: src/widgets/properties_view.ui:96
msgid "File Size"
msgstr "Величина датотеке"
#: src/widgets/properties_view.ui:90
#: src/widgets/properties_view.ui:110
msgid "File Created"
msgstr "Датум настанка"
#: src/widgets/properties_view.ui:100
#: src/widgets/properties_view.ui:120
msgid "File Modified"
msgstr "Датум измене"
#. Translators: This is a label for GPS coordinates
#: src/widgets/properties_view.ui:114
#: src/widgets/properties_view.ui:134
msgid "Location"
msgstr "Место"
#: src/widgets/properties_view.ui:125
#: src/widgets/properties_view.ui:145
msgid "Copy Coordinates"
msgstr "Копирај координате"
#: src/widgets/properties_view.ui:136
#: src/widgets/properties_view.ui:156
msgid "Open Location"
msgstr "Отвори место"
#: src/widgets/properties_view.ui:149
#: src/widgets/properties_view.ui:169
msgid "Originally Created"
msgstr "Време изворног настанка"
#: src/widgets/properties_view.ui:159
#: src/widgets/properties_view.ui:179
msgid "Creator"
msgstr "Творац"
#: src/widgets/properties_view.ui:189
msgid "Copyright"
msgstr "Ауторска права"
#: src/widgets/properties_view.ui:199
msgid "Comment"
msgstr "Напомена"
#: src/widgets/properties_view.ui:169
#: src/widgets/properties_view.ui:209
msgid "Aperture"
msgstr "Бленда"
#: src/widgets/properties_view.ui:179
#: src/widgets/properties_view.ui:219
msgid "Exposure"
msgstr "Излагање"
#: src/widgets/properties_view.ui:189
#: src/widgets/properties_view.ui:229
msgid "ISO"
msgstr "ИСО"
#: src/widgets/properties_view.ui:199
#: src/widgets/properties_view.ui:239
msgid "Focal Length"
msgstr "Жижна даљина"
#: src/widgets/properties_view.ui:209
#: src/widgets/properties_view.ui:249
msgid "Maker, Model"
msgstr "Произвођач и модел"
#: src/widgets/window.rs:206
#: src/widgets/properties_view.ui:259
msgid "Lens"
msgstr "Објектив"
#: src/widgets/properties_view.ui:269
msgid "Software"
msgstr "Програм"
#: src/widgets/properties_view.ui:279
msgid "ICC Profile"
msgstr "ИЦЦ профил"
#: src/widgets/properties_view.ui:289
msgid "CICP"
msgstr "CICP"
#: src/widgets/window.rs:254
msgid "Show Details"
msgstr "Прикажи појединости"

View file

@ -1,4 +1,4 @@
// Copyright (c) 2022-2025 Sophie Herold
// Copyright (c) 2022-2026 Sophie Herold
// Copyright (c) 2022 Christopher Davis
//
// This program is free software: you can redistribute it and/or modify

View file

@ -43,6 +43,7 @@ mod rendering;
mod rotation;
mod scrollable;
mod zoom;
pub mod drawing;
use std::cell::{Cell, OnceCell, RefCell};
use std::marker::PhantomData;
@ -269,6 +270,14 @@ mod imp {
/// Editing queue
pub(super) editing_queue: crate::editing::Queue,
pub(super) operations: RefCell<Option<Arc<Operations>>>,
// Drawing / annotation state
pub(super) drawing_mode: Cell<bool>,
pub(super) draw_tool: Cell<drawing::DrawTool>,
pub(super) draw_color: RefCell<drawing::DrawColor>,
pub(super) draw_line_width: Cell<f64>,
pub(super) strokes: RefCell<Vec<drawing::Stroke>>,
pub(super) current_stroke: RefCell<Option<drawing::CurrentStroke>>,
}
#[glib::object_subclass]
@ -314,6 +323,7 @@ mod imp {
self.zoom_target.set(1.);
self.best_fit.set(true);
self.scaling.set(self.scaling());
self.draw_line_width.set(3.0);
self.connect_input_handling();
@ -408,4 +418,92 @@ impl LpImage {
imp.original_orientation
.set(Some(original_imp.metadata.borrow().orientation()));
}
// ── Drawing / annotation API ──────────────────────────────────────────
pub fn is_drawing_mode(&self) -> bool {
self.imp().drawing_mode.get()
}
pub fn set_drawing_mode(&self, active: bool) {
let imp = self.imp();
imp.drawing_mode.set(active);
if !active {
imp.current_stroke.replace(None);
}
self.queue_draw();
}
pub fn draw_tool(&self) -> drawing::DrawTool {
self.imp().draw_tool.get()
}
pub fn set_draw_tool(&self, tool: drawing::DrawTool) {
self.imp().draw_tool.set(tool);
}
pub fn draw_color(&self) -> drawing::DrawColor {
*self.imp().draw_color.borrow()
}
pub fn set_draw_color(&self, color: drawing::DrawColor) {
self.imp().draw_color.replace(color);
}
pub fn draw_line_width(&self) -> f64 {
self.imp().draw_line_width.get()
}
pub fn set_draw_line_width(&self, width: f64) {
self.imp().draw_line_width.set(width.clamp(1., 50.));
}
pub fn undo_stroke(&self) {
self.imp().strokes.borrow_mut().pop();
self.queue_draw();
}
pub fn clear_strokes(&self) {
self.imp().strokes.borrow_mut().clear();
self.queue_draw();
}
pub fn has_strokes(&self) -> bool {
!self.imp().strokes.borrow().is_empty()
}
/// Render the image with annotations to a texture suitable for saving as PNG.
pub fn render_annotated_texture(&self) -> Option<gdk::Texture> {
let imp = self.imp();
let (w, h) = imp.untransformed_dimensions();
if w == 0 || h == 0 {
return None;
}
let snapshot = gtk::Snapshot::new();
let orientation = self.metadata().orientation();
imp.snapshot_rotate_mirror(
&snapshot,
-(orientation.rotate().degrees() as f32),
orientation.mirror(),
1.0,
);
let render_options = crate::decoder::tiling::RenderOptions {
scaling_filter: gsk::ScalingFilter::Trilinear,
background_color: None,
scaling: 1.,
};
imp.active_frame_buffer()
.add_to_snapshot(&snapshot, 1.0, &render_options);
imp.render_drawing_overlay(&snapshot, 1.0);
let node = snapshot.to_node()?;
let renderer = gsk::CairoRenderer::new();
renderer
.realize_for_display(&gdk::Display::default()?)
.ok()?;
let texture = renderer.render_texture(&node, None);
renderer.unrealize();
Some(texture)
}
}

View file

@ -0,0 +1,216 @@
// Copyright (c) 2026 Loupe contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// SPDX-License-Identifier: GPL-3.0-or-later
use cairo::{Context, LineCap, LineJoin};
use crate::deps::*;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DrawTool {
#[default]
Freeform,
Rectangle,
Circle,
Triangle,
Star,
}
#[derive(Debug, Clone, Copy)]
pub struct DrawColor {
pub r: f64,
pub g: f64,
pub b: f64,
pub a: f64,
}
impl Default for DrawColor {
fn default() -> Self {
Self {
r: 0.96,
g: 0.22,
b: 0.22,
a: 1.0,
}
}
}
impl From<gdk::RGBA> for DrawColor {
fn from(c: gdk::RGBA) -> Self {
Self {
r: c.red() as f64,
g: c.green() as f64,
b: c.blue() as f64,
a: c.alpha() as f64,
}
}
}
impl From<DrawColor> for gdk::RGBA {
fn from(c: DrawColor) -> gdk::RGBA {
gdk::RGBA::new(c.r as f32, c.g as f32, c.b as f32, c.a as f32)
}
}
/// A completed drawing stroke stored in image pixel coordinates.
#[derive(Debug, Clone)]
pub struct Stroke {
pub tool: DrawTool,
/// Freeform: path points. Shapes: [start, end].
pub points: Vec<(f64, f64)>,
pub color: DrawColor,
pub line_width: f64,
}
/// A stroke currently being drawn.
#[derive(Debug, Clone)]
pub struct CurrentStroke {
pub tool: DrawTool,
pub start: (f64, f64),
pub end: (f64, f64),
pub points: Vec<(f64, f64)>,
pub color: DrawColor,
pub line_width: f64,
}
impl Stroke {
pub fn render(&self, cr: &Context) {
cr.set_source_rgba(self.color.r, self.color.g, self.color.b, self.color.a);
cr.set_line_width(self.line_width);
cr.set_line_cap(LineCap::Round);
cr.set_line_join(LineJoin::Round);
let se = || {
(
self.points.first().copied().unwrap_or_default(),
self.points.last().copied().unwrap_or_default(),
)
};
match self.tool {
DrawTool::Freeform => render_freeform(cr, &self.points),
DrawTool::Rectangle => render_rectangle(cr, se()),
DrawTool::Circle => render_ellipse(cr, se()),
DrawTool::Triangle => render_triangle(cr, se()),
DrawTool::Star => render_star(cr, se()),
}
}
}
impl CurrentStroke {
pub fn render(&self, cr: &Context) {
cr.set_source_rgba(self.color.r, self.color.g, self.color.b, self.color.a);
cr.set_line_width(self.line_width);
cr.set_line_cap(LineCap::Round);
cr.set_line_join(LineJoin::Round);
match self.tool {
DrawTool::Freeform => render_freeform(cr, &self.points),
DrawTool::Rectangle => render_rectangle(cr, (self.start, self.end)),
DrawTool::Circle => render_ellipse(cr, (self.start, self.end)),
DrawTool::Triangle => render_triangle(cr, (self.start, self.end)),
DrawTool::Star => render_star(cr, (self.start, self.end)),
}
}
pub fn into_stroke(self) -> Option<Stroke> {
let points = match self.tool {
DrawTool::Freeform => {
if self.points.is_empty() {
return None;
}
self.points
}
_ => {
if (self.end.0 - self.start.0).abs() < 1.
&& (self.end.1 - self.start.1).abs() < 1.
{
return None;
}
vec![self.start, self.end]
}
};
Some(Stroke {
tool: self.tool,
points,
color: self.color,
line_width: self.line_width,
})
}
}
fn render_freeform(cr: &Context, pts: &[(f64, f64)]) {
if pts.is_empty() {
return;
}
cr.move_to(pts[0].0, pts[0].1);
if pts.len() == 1 {
cr.line_to(pts[0].0 + 0.01, pts[0].1);
} else {
for &(x, y) in &pts[1..] {
cr.line_to(x, y);
}
}
let _ = cr.stroke();
}
fn render_rectangle(cr: &Context, ((x1, y1), (x2, y2)): ((f64, f64), (f64, f64))) {
let (x, y, w, h) = (
f64::min(x1, x2),
f64::min(y1, y2),
(x2 - x1).abs(),
(y2 - y1).abs(),
);
if w < 1. || h < 1. {
return;
}
cr.rectangle(x, y, w, h);
let _ = cr.stroke();
}
fn render_ellipse(cr: &Context, ((x1, y1), (x2, y2)): ((f64, f64), (f64, f64))) {
let (cx, cy) = ((x1 + x2) / 2., (y1 + y2) / 2.);
let (rx, ry) = ((x2 - x1).abs() / 2., (y2 - y1).abs() / 2.);
if rx < 1. || ry < 1. {
return;
}
let _ = cr.save();
cr.translate(cx, cy);
cr.scale(rx, ry);
cr.arc(0., 0., 1., 0., 2. * std::f64::consts::PI);
let _ = cr.restore();
let _ = cr.stroke();
}
fn render_triangle(cr: &Context, ((x1, y1), (x2, y2)): ((f64, f64), (f64, f64))) {
cr.move_to((x1 + x2) / 2., y1);
cr.line_to(x2, y2);
cr.line_to(x1, y2);
cr.close_path();
let _ = cr.stroke();
}
fn render_star(cr: &Context, ((x1, y1), (x2, y2)): ((f64, f64), (f64, f64))) {
let (cx, cy) = ((x1 + x2) / 2., (y1 + y2) / 2.);
let outer = f64::min((x2 - x1).abs(), (y2 - y1).abs()) / 2.;
if outer < 1. {
return;
}
let inner = outer * 0.382;
for i in 0..10u32 {
let angle = i as f64 * std::f64::consts::PI / 5. - std::f64::consts::PI / 2.;
let r = if i % 2 == 0 { outer } else { inner };
let (px, py) = (cx + r * angle.cos(), cy + r * angle.sin());
if i == 0 {
cr.move_to(px, py);
} else {
cr.line_to(px, py);
}
}
cr.close_path();
let _ = cr.stroke();
}

View file

@ -21,10 +21,88 @@ use super::*;
impl imp::LpImage {
/// To be called by constructor
pub(super) fn connect_input_handling(&self) {
self.connect_drawing_gesture();
self.connect_controllers();
self.connect_gestures();
}
fn connect_drawing_gesture(&self) {
let obj = self.obj();
let gesture = gtk::GestureDrag::builder().button(1).build();
obj.add_controller(gesture.clone());
gesture.connect_drag_begin(glib::clone!(
#[weak]
obj,
move |g, x, y| {
let imp = obj.imp();
if !imp.drawing_mode.get() {
g.set_state(gtk::EventSequenceState::Denied);
return;
}
g.set_state(gtk::EventSequenceState::Claimed);
let pos = obj.widget_to_img_coord((x, y));
imp.current_stroke.replace(Some(drawing::CurrentStroke {
tool: imp.draw_tool.get(),
start: pos,
end: pos,
points: vec![pos],
color: *imp.draw_color.borrow(),
line_width: imp.draw_line_width.get(),
}));
obj.queue_draw();
}
));
gesture.connect_drag_update(glib::clone!(
#[weak]
obj,
move |g, dx, dy| {
let imp = obj.imp();
if !imp.drawing_mode.get() {
return;
}
let Some((sx, sy)) = g.start_point() else { return };
let pos = obj.widget_to_img_coord((sx + dx, sy + dy));
let mut cur = imp.current_stroke.borrow_mut();
if let Some(s) = cur.as_mut() {
s.end = pos;
if s.tool == drawing::DrawTool::Freeform {
s.points.push(pos);
}
}
drop(cur);
obj.queue_draw();
}
));
gesture.connect_drag_end(glib::clone!(
#[weak]
obj,
move |g, dx, dy| {
let imp = obj.imp();
if !imp.drawing_mode.get() {
return;
}
let mut cur = imp.current_stroke.replace(None);
if let Some(s) = cur.as_mut() {
if let Some((sx, sy)) = g.start_point() {
let pos = obj.widget_to_img_coord((sx + dx, sy + dy));
s.end = pos;
if s.tool == drawing::DrawTool::Freeform {
s.points.push(pos);
}
}
}
if let Some(stroke) = cur.and_then(|s| s.into_stroke()) {
imp.strokes.borrow_mut().push(stroke);
}
obj.queue_draw();
}
));
}
fn connect_controllers(&self) {
let obj = self.obj();
@ -178,6 +256,12 @@ impl imp::LpImage {
tracing::trace!("Drag gesture begin");
let imp = obj.imp();
// Yield to the drawing gesture when in drawing mode
if imp.drawing_mode.get() {
gesture.set_state(gtk::EventSequenceState::Denied);
return;
}
// Allow only left and middle button
if ![1, 2].contains(&gesture.current_button())
// Drag gesture for touchscreens is handled by ScrolledWindow

View file

@ -139,6 +139,9 @@ impl WidgetImpl for imp::LpImage {
);
frame_buffer.add_to_snapshot(snapshot, applicable_zoom, &render_options);
// Drawing / annotation overlay
self.render_drawing_overlay(snapshot, applicable_zoom);
}
snapshot.restore();
@ -405,3 +408,36 @@ impl LpImage {
(x, y)
}
}
impl imp::LpImage {
/// Render completed and in-progress drawing strokes on top of the image.
///
/// Must be called after the frame buffer has been added to the snapshot
/// (in the non-operations render path) while the image coordinate transforms
/// are still active.
pub(super) fn render_drawing_overlay(&self, snapshot: &gtk::Snapshot, zoom: f64) {
let strokes = self.strokes.borrow();
let current = self.current_stroke.borrow();
if strokes.is_empty() && current.is_none() {
return;
}
let (unw, unh) = self.untransformed_dimensions();
if unw == 0 || unh == 0 {
return;
}
let w = (unw as f64 * zoom) as f32;
let h = (unh as f64 * zoom) as f32;
let cr = snapshot.append_cairo(&graphene::Rect::new(0., 0., w, h));
cr.scale(zoom, zoom);
for stroke in strokes.iter() {
stroke.render(&cr);
}
if let Some(c) = current.as_ref() {
c.render(&cr);
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2022-2025 Sophie Herold
// Copyright (c) 2022-2026 Sophie Herold
// Copyright (c) 2022, 2024 Christopher Davis
//
// This program is free software: you can redistribute it and/or modify

View file

@ -111,6 +111,28 @@ mod imp {
pub(super) show_controls_animation: OnceCell<adw::TimedAnimation>,
pub(super) hide_controls_animation: OnceCell<adw::TimedAnimation>,
pub(super) hide_controls_timeout: RefCell<Option<glib::SourceId>>,
// Drawing toolbar template children
#[template_child]
pub(super) draw_mode_button: TemplateChild<gtk::ToggleButton>,
#[template_child]
pub(super) draw_toolbar_revealer: TemplateChild<gtk::Revealer>,
#[template_child]
pub(super) draw_freeform_btn: TemplateChild<gtk::ToggleButton>,
#[template_child]
pub(super) draw_rect_btn: TemplateChild<gtk::ToggleButton>,
#[template_child]
pub(super) draw_circle_btn: TemplateChild<gtk::ToggleButton>,
#[template_child]
pub(super) draw_triangle_btn: TemplateChild<gtk::ToggleButton>,
#[template_child]
pub(super) draw_star_btn: TemplateChild<gtk::ToggleButton>,
#[template_child]
pub(super) draw_color_btn: TemplateChild<gtk::ColorDialogButton>,
#[template_child]
pub(super) draw_line_width_btn: TemplateChild<gtk::SpinButton>,
#[template_child]
pub(super) draw_clear_btn: TemplateChild<gtk::Button>,
}
#[glib::object_subclass]
@ -437,6 +459,131 @@ mod imp {
true
}
));
// ── Drawing toolbar setup ──────────────────────────────────────
{
let imp = self;
// Group tool toggle buttons (mutually exclusive)
imp.draw_rect_btn.set_group(Some(&*imp.draw_freeform_btn));
imp.draw_circle_btn.set_group(Some(&*imp.draw_freeform_btn));
imp.draw_triangle_btn.set_group(Some(&*imp.draw_freeform_btn));
imp.draw_star_btn.set_group(Some(&*imp.draw_freeform_btn));
imp.draw_freeform_btn.set_active(true);
// Set initial color to match DrawColor::default() (red)
imp.draw_color_btn
.set_rgba(&gdk::RGBA::new(0.96, 0.22, 0.22, 1.0));
// Connect draw mode button (headerbar toggle)
imp.draw_mode_button.connect_toggled(glib::clone!(
#[weak]
obj,
move |btn| {
let active = btn.is_active();
let imp = obj.imp();
imp.draw_toolbar_revealer.set_reveal_child(active);
if let Some(img) = imp.image_view.current_image() {
img.set_drawing_mode(active);
if active {
let color = crate::widgets::image::drawing::DrawColor::from(
imp.draw_color_btn.rgba(),
);
img.set_draw_color(color);
img.set_draw_line_width(imp.draw_line_width_btn.value());
}
}
}
));
// Tool buttons
imp.draw_freeform_btn.connect_toggled(glib::clone!(
#[weak]
obj,
move |btn| {
if btn.is_active() {
obj.set_draw_tool(
crate::widgets::image::drawing::DrawTool::Freeform,
);
}
}
));
imp.draw_rect_btn.connect_toggled(glib::clone!(
#[weak]
obj,
move |btn| {
if btn.is_active() {
obj.set_draw_tool(
crate::widgets::image::drawing::DrawTool::Rectangle,
);
}
}
));
imp.draw_circle_btn.connect_toggled(glib::clone!(
#[weak]
obj,
move |btn| {
if btn.is_active() {
obj.set_draw_tool(
crate::widgets::image::drawing::DrawTool::Circle,
);
}
}
));
imp.draw_triangle_btn.connect_toggled(glib::clone!(
#[weak]
obj,
move |btn| {
if btn.is_active() {
obj.set_draw_tool(
crate::widgets::image::drawing::DrawTool::Triangle,
);
}
}
));
imp.draw_star_btn.connect_toggled(glib::clone!(
#[weak]
obj,
move |btn| {
if btn.is_active() {
obj.set_draw_tool(
crate::widgets::image::drawing::DrawTool::Star,
);
}
}
));
// Color button
imp.draw_color_btn.connect_rgba_notify(glib::clone!(
#[weak]
obj,
move |btn| {
let color =
crate::widgets::image::drawing::DrawColor::from(btn.rgba());
obj.set_draw_color(color);
}
));
// Line width spin button
imp.draw_line_width_btn.connect_value_changed(glib::clone!(
#[weak]
obj,
move |spin| {
obj.set_draw_line_width(spin.value());
}
));
// Clear all strokes button
imp.draw_clear_btn.connect_clicked(glib::clone!(
#[weak]
obj,
move |_| {
if let Some(img) = obj.imp().image_view.current_image() {
img.clear_strokes();
}
}
));
}
}
}
@ -607,6 +754,25 @@ impl LpImageWindow {
pub fn copy_image(&self) {
let imp = self.imp();
// When annotations exist, copy the flattened texture instead of the file
if let Some(img) = imp.image_view.current_image() {
if img.has_strokes() {
if let Some(texture) = img.render_annotated_texture() {
let content =
gdk::ContentProvider::for_value(&texture.to_value());
if let Err(e) = self.clipboard().set_content(Some(&content)) {
tracing::error!("Failed to copy annotated image: {e}");
} else {
self.window_show_toast(
&gettext("Image copied to clipboard"),
adw::ToastPriority::High,
);
}
return;
}
}
}
if let Err(e) = imp.image_view.copy() {
tracing::error!("Failed to copy to clipboard: {}", e);
} else {
@ -1006,4 +1172,69 @@ impl LpImageWindow {
fn headerbar_opacity(&self) -> f64 {
self.imp().headerbar.opacity()
}
// ── Drawing helpers ───────────────────────────────────────────────────
pub fn toggle_draw_mode(&self) {
let imp = self.imp();
let new_state = !imp.draw_mode_button.is_active();
imp.draw_mode_button.set_active(new_state);
// The `connect_toggled` handler on `draw_mode_button` takes care of the rest.
}
pub fn set_draw_tool(&self, tool: crate::widgets::image::drawing::DrawTool) {
if let Some(img) = self.imp().image_view.current_image() {
img.set_draw_tool(tool);
}
}
pub fn set_draw_color(&self, color: crate::widgets::image::drawing::DrawColor) {
if let Some(img) = self.imp().image_view.current_image() {
img.set_draw_color(color);
}
}
pub fn set_draw_line_width(&self, width: f64) {
if let Some(img) = self.imp().image_view.current_image() {
img.set_draw_line_width(width);
}
}
pub async fn draw_save_as(&self) {
let Some(img) = self.imp().image_view.current_image() else {
return;
};
let Some(current_file) = img.file() else {
return;
};
let file_dialog = gtk::FileDialog::new();
if let Some(path) = current_file.path() {
if let Some(stem) = path.file_stem() {
let mut new_name = stem.to_os_string();
new_name.push("-annotated.png");
let mut suggested = path.clone();
suggested.set_file_name(new_name);
file_dialog
.set_initial_file(Some(&gio::File::for_path(suggested)));
}
}
let window = self.try_window();
let Ok(save_file) = file_dialog.save_future(window.as_ref()).await else {
return;
};
let Some(texture) = img.render_annotated_texture() else {
tracing::error!("Failed to render annotated texture");
return;
};
if let Some(path) = save_file.path() {
let bytes = texture.save_to_png_bytes();
if let Err(err) = std::fs::write(&path, &*bytes) {
tracing::error!("Failed to save annotated image: {err}");
}
}
}
}

View file

@ -132,6 +132,15 @@
<property name="tooltip-text" translatable="yes">Edit Image</property>
</object>
</child>
<child type="end">
<object class="GtkToggleButton" id="draw_mode_button">
<property name="icon-name">draw-freehand-symbolic</property>
<property name="tooltip-text" translatable="yes">Annotate Image (D)</property>
<style>
<class name="flat" />
</style>
</object>
</child>
<child>
<object class="GtkEventControllerMotion" id="headerbar_events" />
</child>
@ -139,6 +148,144 @@
</property>
</object>
</child>
<child type="bottom">
<object class="GtkRevealer" id="draw_toolbar_revealer">
<property name="reveal-child">False</property>
<property name="transition-type">slide-up</property>
<property name="child">
<object class="GtkBox">
<property name="spacing">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<style>
<class name="toolbar" />
</style>
<!-- Tool selector (linked toggle buttons) -->
<child>
<object class="GtkBox">
<property name="spacing">0</property>
<style>
<class name="linked" />
</style>
<child>
<object class="GtkToggleButton" id="draw_freeform_btn">
<property name="icon-name">draw-freehand-symbolic</property>
<property name="tooltip-text" translatable="yes">Freeform</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="draw_rect_btn">
<property name="label">Rect</property>
<property name="tooltip-text" translatable="yes">Rectangle</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="draw_circle_btn">
<property name="label">Circle</property>
<property name="tooltip-text" translatable="yes">Circle / Ellipse</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="draw_triangle_btn">
<property name="label">Tri</property>
<property name="tooltip-text" translatable="yes">Triangle</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="draw_star_btn">
<property name="label">Star</property>
<property name="tooltip-text" translatable="yes">Star</property>
</object>
</child>
</object>
</child>
<!-- Separator -->
<child>
<object class="GtkSeparator">
<property name="orientation">vertical</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
</object>
</child>
<!-- Stroke color -->
<child>
<object class="GtkColorDialogButton" id="draw_color_btn">
<property name="tooltip-text" translatable="yes">Stroke Color</property>
<property name="dialog">
<object class="GtkColorDialog" />
</property>
</object>
</child>
<!-- Line width -->
<child>
<object class="GtkSpinButton" id="draw_line_width_btn">
<property name="tooltip-text" translatable="yes">Line Width</property>
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">1</property>
<property name="upper">50</property>
<property name="value">3</property>
<property name="step-increment">1</property>
<property name="page-increment">5</property>
</object>
</property>
<property name="digits">0</property>
<property name="numeric">True</property>
<property name="width-chars">4</property>
</object>
</child>
<!-- Separator -->
<child>
<object class="GtkSeparator">
<property name="orientation">vertical</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
</object>
</child>
<!-- Undo last stroke -->
<child>
<object class="GtkButton">
<property name="icon-name">edit-undo-symbolic</property>
<property name="tooltip-text" translatable="yes">Undo Last Stroke (Ctrl+Z)</property>
<property name="action-name">win.draw-undo</property>
<style>
<class name="flat" />
</style>
</object>
</child>
<!-- Clear all -->
<child>
<object class="GtkButton" id="draw_clear_btn">
<property name="icon-name">edit-clear-all-symbolic</property>
<property name="tooltip-text" translatable="yes">Clear All Annotations</property>
<style>
<class name="flat" />
</style>
</object>
</child>
<!-- Spacer -->
<child>
<object class="GtkBox">
<property name="hexpand">True</property>
</object>
</child>
<!-- Save annotated copy -->
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Save Copy…</property>
<property name="tooltip-text" translatable="yes">Save annotated copy as PNG</property>
<property name="action-name">win.draw-save-as</property>
<style>
<class name="suggested-action" />
</style>
</object>
</child>
</object>
</property>
</object>
</child>
<property name="content">
<object class="AdwMultiLayoutView">
<binding name="layout-name">

View file

@ -91,6 +91,12 @@ pub enum Action {
Reload,
#[strum(to_string = "win.edit")]
Edit,
#[strum(to_string = "win.draw-toggle")]
DrawToggle,
#[strum(to_string = "win.draw-undo")]
DrawUndo,
#[strum(to_string = "win.draw-save-as")]
DrawSaveAs,
}
impl Action {
@ -293,6 +299,28 @@ impl Action {
obj.window_inspect(|w| w.show_edit());
});
}
Action::DrawToggle => {
klass.install_action(&action, None, move |win, _, _| {
win.toggle_draw_mode();
});
}
Action::DrawUndo => {
klass.install_action(&action, None, move |win, _, _| {
if let Some(img) = win.imp().image_view.current_image() {
if img.is_drawing_mode() {
img.undo_stroke();
}
}
});
}
Action::DrawSaveAs => {
klass.install_action_async(&action, None, |win, _, _| async move {
win.draw_save_as().await;
});
}
}
}
}
@ -371,6 +399,8 @@ impl Action {
Action::ToggleProperties => &[(Key::F9, &[]), (Key::Return, &[ModifierType::ALT_MASK])],
Action::Reload => &[(Key::F5, &[])],
Action::Edit => &[(Key::e, &[])],
Action::DrawToggle => &[(Key::d, &[])],
Action::DrawUndo => &[(Key::z, &[ModifierType::CONTROL_MASK])],
_ => &[],
}
}