From 8148988eb86979b72fedb41ccf0fda7e47b89dce Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 7 Oct 2026 09:16:43 -0400 Subject: [PATCH 001/213] Docs: allow Noto CJK (via craft-fonts) for Chinese text Maintainer decision: Noto Sans/Serif CJK is Google-branded Source Han (Adobe co-developed); nearly every OFL Chinese face derives from it, and the Simplified/Traditional Chinese UI (#184, #198) uses Noto Sans CJK SC from craft-fonts. Record it as a narrow, explicit exception to the Adobe-asset rule. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 7 +++++-- CLAUDE.md | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 393db2b12..597e207f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,7 +63,10 @@ of it. Full standard: `../craftrules/standards/never-crash.md` - **Never use any iconography, image, artwork, font, sound or other asset from Adobe products** (no Lightroom/Creative Cloud icons, no screenshots, no presets/profiles/LUTs, no UI bitmaps — not even as a temporary placeholder or "reference copy"). Observing Adobe's UI to imitate *layout and behaviour* is allowed; copying or tracing its assets is not. - **This includes Adobe's open-licensed assets**: no Source Sans/Serif/Code or Source Han fonts, no Adobe Fonts, no Adobe-published icon sets, sample photos, colour profiles or LUTs — even when OFL/MIT. The UI font is Inter (OFL); - Japanese fonts come from craft-fonts (below). + Japanese and Chinese fonts come from craft-fonts (below). **One exception (maintainer decision, 2026-10-07): Noto + Sans/Serif CJK** (Google-branded, OFL, co-developed with Adobe as Source Han) is allowed via craft-fonts for Chinese + text, because nearly every OFL Chinese face derives from it. Use it unmodified under its OFL; this does not open the + door to Source Han under Adobe's name or to any other Adobe asset. - Every asset in the repository must be one of: **our own original work** (e.g. icons drawn in code as vectors, procedurally generated demo photos), **public domain / CC0**, **Creative Commons** (CC-BY / CC-BY-SA with attribution honoured), **OFL** (fonts), or **permissive open-source** (MIT/Apache-2.0/BSD/ISC) — or contributed by a person who created the asset and licenses it openly. - **Exception: `docs/brand/`.** The ArtCraft name, wordmark and logos there are ArtCraft Team trademarks, not open source and not covered by LightCraft's MIT OR Apache-2.0 licence (`LICENSE-MIT`, `LICENSE-APACHE`, `NOTICE`); their terms are in `docs/brand/LICENSE-brand.txt`. Use them only unmodified and never redraw, recolour or derive from them. - **Every asset must have an entry in `assets/ATTRIBUTION.md`** (path, title, author/creator, source URL or "original work", licence, date added, modifications) and its licence text when required (e.g. `assets/fonts/OFL-*.txt`). Add the entry in the same commit as the asset. Assets without an attribution entry must not be committed. @@ -72,7 +75,7 @@ of it. Full standard: `../craftrules/standards/never-crash.md` uses it as the optional build input `CRAFT_FONTS_DIR`: `git clone https://github.com/storytold/craft-fonts ../craft-fonts` then `CRAFT_FONTS_DIR=../craft-fonts cargo run -p lightcraft` (or any cargo/xtask command). `crates/engine/build.rs` embeds the manifest's fonts as `lightcraft_engine::CRAFT_FONTS` (wasm32: BIZ UDPGothic Regular only); the UI - (`theme::font_definitions`) and the export watermark renderer use its Japanese faces as fallbacks after Inter. Unset, + (`theme::font_definitions`) and the export watermark renderer use its CJK faces (picked by script) as fallbacks after Inter. Unset, `CRAFT_FONTS` is empty: everything builds, tests and runs, but Japanese text has no glyphs. Releases always build with it (`release.yml`, `CRAFT_FONTS_REQUIRED=1`) and ship the fonts' OFL licences. Tests that need these fonts skip without it; the FreeBSD CI job runs them with it. Rules: craftrules diff --git a/CLAUDE.md b/CLAUDE.md index 393db2b12..597e207f0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,7 +63,10 @@ of it. Full standard: `../craftrules/standards/never-crash.md` - **Never use any iconography, image, artwork, font, sound or other asset from Adobe products** (no Lightroom/Creative Cloud icons, no screenshots, no presets/profiles/LUTs, no UI bitmaps — not even as a temporary placeholder or "reference copy"). Observing Adobe's UI to imitate *layout and behaviour* is allowed; copying or tracing its assets is not. - **This includes Adobe's open-licensed assets**: no Source Sans/Serif/Code or Source Han fonts, no Adobe Fonts, no Adobe-published icon sets, sample photos, colour profiles or LUTs — even when OFL/MIT. The UI font is Inter (OFL); - Japanese fonts come from craft-fonts (below). + Japanese and Chinese fonts come from craft-fonts (below). **One exception (maintainer decision, 2026-10-07): Noto + Sans/Serif CJK** (Google-branded, OFL, co-developed with Adobe as Source Han) is allowed via craft-fonts for Chinese + text, because nearly every OFL Chinese face derives from it. Use it unmodified under its OFL; this does not open the + door to Source Han under Adobe's name or to any other Adobe asset. - Every asset in the repository must be one of: **our own original work** (e.g. icons drawn in code as vectors, procedurally generated demo photos), **public domain / CC0**, **Creative Commons** (CC-BY / CC-BY-SA with attribution honoured), **OFL** (fonts), or **permissive open-source** (MIT/Apache-2.0/BSD/ISC) — or contributed by a person who created the asset and licenses it openly. - **Exception: `docs/brand/`.** The ArtCraft name, wordmark and logos there are ArtCraft Team trademarks, not open source and not covered by LightCraft's MIT OR Apache-2.0 licence (`LICENSE-MIT`, `LICENSE-APACHE`, `NOTICE`); their terms are in `docs/brand/LICENSE-brand.txt`. Use them only unmodified and never redraw, recolour or derive from them. - **Every asset must have an entry in `assets/ATTRIBUTION.md`** (path, title, author/creator, source URL or "original work", licence, date added, modifications) and its licence text when required (e.g. `assets/fonts/OFL-*.txt`). Add the entry in the same commit as the asset. Assets without an attribution entry must not be committed. @@ -72,7 +75,7 @@ of it. Full standard: `../craftrules/standards/never-crash.md` uses it as the optional build input `CRAFT_FONTS_DIR`: `git clone https://github.com/storytold/craft-fonts ../craft-fonts` then `CRAFT_FONTS_DIR=../craft-fonts cargo run -p lightcraft` (or any cargo/xtask command). `crates/engine/build.rs` embeds the manifest's fonts as `lightcraft_engine::CRAFT_FONTS` (wasm32: BIZ UDPGothic Regular only); the UI - (`theme::font_definitions`) and the export watermark renderer use its Japanese faces as fallbacks after Inter. Unset, + (`theme::font_definitions`) and the export watermark renderer use its CJK faces (picked by script) as fallbacks after Inter. Unset, `CRAFT_FONTS` is empty: everything builds, tests and runs, but Japanese text has no glyphs. Releases always build with it (`release.yml`, `CRAFT_FONTS_REQUIRED=1`) and ship the fonts' OFL licences. Tests that need these fonts skip without it; the FreeBSD CI job runs them with it. Rules: craftrules From 1ba0421a85702bf0142bdcbd33fd2734fa923df4 Mon Sep 17 00:00:00 2001 From: radialmonster Date: Wed, 7 Oct 2026 21:58:09 -0400 Subject: [PATCH 002/213] =?UTF-8?q?LR-IMP-FORMATS:=20RW2=20format=208:=20d?= =?UTF-8?q?ecode=20each=202=C3=972=20cell=20column=20by=20column?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raw format 8 codes a cell top-left, bottom-left, top-right, bottom-right. decode_strip placed the values in row order, which put the two green samples of every RGGB cell on each other's sites: flat areas were right, but every edge got a one-pixel colour zipper. Measured on a G9 II and the corpus GH6, S5 II and S9: the Gb plane sat at (+0.5, -0.5) half-res cells from Gr instead of the Bayer (-0.5, +0.5); with this change all four match. The test encoder used the same order, so the round trip passed either way; a new known-answer test hand-codes one cell and checks where it lands. Co-Authored-By: Claude Opus 5.5 --- crates/raw/src/vendor/rw2.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/crates/raw/src/vendor/rw2.rs b/crates/raw/src/vendor/rw2.rs index 10ade6272..0854b6105 100644 --- a/crates/raw/src/vendor/rw2.rs +++ b/crates/raw/src/vendor/rw2.rs @@ -43,7 +43,9 @@ //! reads as repeats of one code only in this order; MSB-first reading gives code frequencies of exactly //! 2^−length, the signature of random bits]. Tag `0x0040` lists (length, code) for 17 prefix codes, the difference //! categories 0–16 of ITU-T T.81 (category `c`, then `c` additional bits, a leading 0 meaning negative). Pixels -//! are coded two rows at a time, cell by 2×2 cell (top-left, top-right, bottom-left, bottom-right); each is +//! are coded two rows at a time, cell by 2×2 cell, column by column (top-left, bottom-left, top-right, +//! bottom-right) [in row order the two greens trade sites: an edge's even and odd rows of green then sit ~2 px +//! apart with red and blue between them]; each is //! predicted from the same site of the previous cell, the first cell of a row pair from the first cell of the //! previous pair, 0 at the top [the strips decode to the camera JPEG's image without seams]. The streams end a //! few padding rows before their stated length. Tags `0x0039`–`0x003f`, `0x0041` and `0x0043` carry the same @@ -401,7 +403,7 @@ fn decode_strip(s: &Strip, codes: &Huffman, mut rows: Vec<&mut [u16]>) -> Result for (k, v) in cell.iter_mut().enumerate() { let d = diff(&mut bits, codes).ok_or_else(|| RawError::Corrupt(format!("RW2 format 8: invalid code in row {}", 2 * pair)))?; *v = v.saturating_add(d); - if let Some(px) = rows.get_mut(2 * pair + k / 2).and_then(|r| r.get_mut(2 * cx + k % 2)) { + if let Some(px) = rows.get_mut(2 * pair + k % 2).and_then(|r| r.get_mut(2 * cx + k / 2)) { *px = (*v).clamp(0, u16::MAX as i32) as u16; } } @@ -962,7 +964,7 @@ mod tests { let mut cell = first; for cx in 0..w / 2 { for (k, p) in cell.iter_mut().enumerate() { - let v = at(2 * cx + k % 2, 2 * pair + k / 2); + let v = at(2 * cx + k / 2, 2 * pair + k % 2); let d = v - *p; let c = 32 - d.unsigned_abs().leading_zeros(); let (len, code) = table[c as usize]; @@ -1026,6 +1028,26 @@ mod tests { } } + #[test] + fn format8_cell_is_coded_column_by_column() { + // One 2×2 cell coded by hand (not with `encode_strip`, which shares the decoder's site order): the values + // 1, 2, 3, 4 in stream order. Row order would put the two greens of an RGGB cell on each other's sites. + let mut bits: Vec = vec![]; + let mut put = |n: u32, v: u32| bits.extend((0..n).rev().map(|i| v >> i & 1 == 1)); + for v in [1u32, 2, 3, 4] { + let c = 32 - v.leading_zeros(); + let (len, code) = TABLE14[c as usize]; + put(len, code); + put(c, v); + } + let n = bits.len() as u64; + let mut data: Vec = bits.chunks(8).map(|b| b.iter().enumerate().fold(0u8, |a, (i, &x)| a | (x as u8) << i)).collect(); + data.extend([0; 8]); + let (mut top, mut bottom) = ([0u16; 2], [0u16; 2]); + decode_strip(&Strip { data: &data, width: 2, height: 2, bits: n }, &code_table(&TABLE14).unwrap(), vec![&mut top, &mut bottom]).unwrap(); + assert_eq!((top, bottom), ([1, 3], [2, 4]), "top-left, bottom-left, top-right, bottom-right"); + } + fn replace(tags: &mut Vec<(u16, Value)>, tag: u16, v: Value) { tags.retain(|(t, _)| *t != tag); tags.push((tag, v)); From 024e983a7ee65781562071a0d2c479b128ef0e1f Mon Sep 17 00:00:00 2001 From: Hanmo Date: Thu, 8 Oct 2026 10:16:44 +0800 Subject: [PATCH 003/213] M3: honour Nikon CropArea and embedded preview colour space --- crates/codecs/README.md | 4 +++ crates/codecs/src/jpeg.rs | 47 +++++++++++++++++++++++- crates/codecs/src/lib.rs | 8 +++++ crates/engine/src/camera_preview.rs | 3 +- crates/engine/src/files.rs | 21 ++++++++--- crates/engine/src/media.rs | 2 +- crates/raw/src/lib.rs | 2 +- crates/raw/src/preview.rs | 24 +++++++++++++ crates/raw/src/vendor/nef.rs | 56 +++++++++++++++++++++++++++-- docs/camera-preview-colour.md | 9 +++++ docs/parity.md | 4 +-- 11 files changed, 166 insertions(+), 14 deletions(-) diff --git a/crates/codecs/README.md b/crates/codecs/README.md index d826b5ec1..05824eb02 100644 --- a/crates/codecs/README.md +++ b/crates/codecs/README.md @@ -56,6 +56,10 @@ or CMYK profile converted by the CMS straight to linear Rec.2020), `Container` ( EXIF `R03` Adobe RGB hint), `Untagged` (assumed sRGB; untagged float data assumed linear), `IccUnsupported` (profile present but unusable → **sRGB fallback**), `Naive` (CMYK without profile). +`decode_jpeg_with_fallback(bytes, opts, space)` lets an enclosing RAW container supply the colour +space of a JPEG with no ICC or EXIF metadata. It applies the transfer curve before linear-light +resizing; a JPEG carrying its own metadata retains its existing interpretation. + ICC profiles are parsed with `moxcms` (v2/v4, `curv`/`para` TRCs, `chad`); sRGB, Display P3, Adobe RGB (1998), ProPhoto (ROMM) and Rec.2020 are recognised by colorants. `icc::write_matrix_trc` emits v4 display profiles (D50 PCS, Bradford `chad`) for export embedding. diff --git a/crates/codecs/src/jpeg.rs b/crates/codecs/src/jpeg.rs index 0096f9059..ce8712ade 100644 --- a/crates/codecs/src/jpeg.rs +++ b/crates/codecs/src/jpeg.rs @@ -105,6 +105,10 @@ pub(crate) fn parse_markers(b: &[u8]) -> Option { } pub(crate) fn decode(bytes: &[u8], opts: &DecodeOptions) -> Result { + decode_with_fallback(bytes, opts, None) +} + +pub(crate) fn decode_with_fallback(bytes: &[u8], opts: &DecodeOptions, fallback: Option) -> Result { let m = parse_markers(bytes).ok_or_else(|| Error::Malformed(F, "missing SOI".into()))?; if m.components == 0 { return Err(Error::Malformed(F, "no frame header".into())); @@ -119,7 +123,10 @@ pub(crate) fn decode(bytes: &[u8], opts: &DecodeOptions) -> Result { } else { decode_zune(bytes, &m)? }; - let meta = Meta { icc: m.icc, exif: m.exif, xmp: m.xmp, ..Default::default() }; + // A raw container may carry the colour declaration of an otherwise untagged preview. + // Apply it before linearization/resizing; existing JPEG metadata always wins. + let hint = fallback.filter(|_| m.icc.is_none() && m.exif.is_none()).map(|space| crate::SourceSpace::named(space, crate::SpaceOrigin::Container)); + let meta = Meta { icc: m.icc, exif: m.exif, xmp: m.xmp, hint, ..Default::default() }; finish(F, raw, meta, (m.width, m.height), opts) } @@ -246,6 +253,44 @@ fn mpf_images(m: &Markers) -> Vec<(usize, usize)> { mod tests { use super::*; + #[test] + fn container_fallback_matches_tagged_jpeg_before_resizing_and_keeps_own_metadata() { + use crate::{ChromaSubsampling, EncodeImage, EncodeMeta, NamedSpace, Samples, SpaceOrigin, encode_jpeg}; + let pixels: Vec = (0..16 * 16).flat_map(|i| if i % 2 == 0 { [120, 180, 90] } else { [30, 100, 200] }).collect(); + let encode = |icc, exif| { + encode_jpeg( + &EncodeImage::new(16, 16, 3, Samples::U8(&pixels)), + 100, + ChromaSubsampling::S444, + &EncodeMeta { icc, exif, ..Default::default() }, + ) + .unwrap() + }; + let untagged = encode(None, None); + let adobe = crate::icc::write_named(NamedSpace::AdobeRgb); + let tagged = encode(Some(&adobe), None); + for opts in [DecodeOptions::default(), DecodeOptions::fit(3, 3)] { + let with = crate::decode_jpeg_with_fallback(&untagged, opts, NamedSpace::AdobeRgb).unwrap(); + let expected = crate::decode(&tagged, opts).unwrap(); + assert_eq!(with.space.origin, SpaceOrigin::Container); + let (a, b) = (with.to_working(), expected.to_working()); + for (p, q) in a.data.iter().zip(&b.data) { + assert!(p.iter().zip(q).all(|(a, b)| (a - b).abs() < 0.0001), "{p:?} != {q:?}"); + } + } + let srgb = crate::icc::write_named(NamedSpace::Srgb); + let exif = crate::exif::minimal_exif(6); + for bytes in [encode(Some(&srgb), None), encode(None, Some(&exif)), encode(Some(b"bad ICC"), None)] { + let opts = DecodeOptions::fit(5, 5); + let with = crate::decode_jpeg_with_fallback(&bytes, opts, NamedSpace::AdobeRgb).unwrap(); + let expected = crate::decode(&bytes, opts).unwrap(); + assert_eq!(with.space, expected.space); + assert_eq!(with.image, expected.image); + assert_eq!(with.orientation, expected.orientation); + } + assert!(crate::decode_jpeg_with_fallback(b"bad JPEG", DecodeOptions::default(), NamedSpace::AdobeRgb).is_err()); + } + #[test] fn scaled_request_covers() { assert_eq!(scaled_request(6000, 4000, 256, 256), (256, 171)); diff --git a/crates/codecs/src/lib.rs b/crates/codecs/src/lib.rs index 708aa9b79..0328f33c0 100644 --- a/crates/codecs/src/lib.rs +++ b/crates/codecs/src/lib.rs @@ -156,6 +156,14 @@ pub fn decode(bytes: &[u8], opts: DecodeOptions) -> Result { std::panic::catch_unwind(|| decode_unguarded(bytes, format, &opts)).unwrap_or_else(|_| Err(Error::Malformed(format, "decoder panicked".into()))) } +/// Decode a JPEG preview with its enclosing container's colour space as a fallback. +/// A JPEG carrying ICC or EXIF metadata keeps its own interpretation. The fallback is applied +/// before transfer decoding and linear-light resizing, never to already-resampled pixels. +pub fn decode_jpeg_with_fallback(bytes: &[u8], opts: DecodeOptions, fallback: NamedSpace) -> Result { + std::panic::catch_unwind(|| jpeg::decode_with_fallback(bytes, &opts, Some(fallback))) + .unwrap_or_else(|_| Err(Error::Malformed(Format::Jpeg, "decoder panicked".into()))) +} + /// [`decode`] without the panic guard (for fuzzing our own code paths). #[doc(hidden)] pub fn decode_unguarded(bytes: &[u8], format: Format, opts: &DecodeOptions) -> Result { diff --git a/crates/engine/src/camera_preview.rs b/crates/engine/src/camera_preview.rs index cd0d475d2..fed75bebc 100644 --- a/crates/engine/src/camera_preview.rs +++ b/crates/engine/src/camera_preview.rs @@ -60,9 +60,8 @@ pub(crate) fn fit_preview(raw: &RawImage, bytes: &[u8], transform: &CameraTransf /// Same-size proxies of the sensor (white-balanced, baseline exposure, through `transform`'s /// matrix: the generic camera ≈ sRGB model) and of the file's embedded camera JPEG (linear Rec.2020). fn proxies(raw: &RawImage, bytes: &[u8], transform: &CameraTransform, size: usize) -> Option<(Rgb32f, Rgb32f)> { - let jpeg = lightcraft_raw::embedded_preview(bytes)?; let edge = (2 * size).max(384) as u32; - let decoded = lightcraft_codecs::decode(&jpeg, lightcraft_codecs::DecodeOptions { max_size: Some((edge, edge)), max_pixels: 64_000_000 }).ok()?; + let decoded = crate::files::decode_raw_preview(bytes, lightcraft_codecs::DecodeOptions { max_size: Some((edge, edge)), max_pixels: 64_000_000 })?; let mut reference = decoded.to_working(); let (a, crop) = (raw.active_area, raw.crop.clipped(raw.active_area.width, raw.active_area.height)); if crop.width == 0 || crop.height == 0 || reference.width == 0 || reference.height == 0 { diff --git a/crates/engine/src/files.rs b/crates/engine/src/files.rs index 62a1c467f..183efceec 100644 --- a/crates/engine/src/files.rs +++ b/crates/engine/src/files.rs @@ -320,6 +320,21 @@ fn preview_orientation(raw_bytes: &[u8], jpeg_orientation: u16) -> Orientation { lightcraft_meta::extract(raw_bytes).orientation.unwrap_or(Orientation::Normal) } +/// Shared colour interpretation for quick previews, unsupported RAW fallback and camera-look fitting. +pub(crate) fn decode_raw_preview(bytes: &[u8], opts: lightcraft_codecs::DecodeOptions) -> Option { + let jpeg = lightcraft_raw::embedded_preview(bytes)?; + match lightcraft_raw::embedded_preview_color_space(bytes) { + Some(space) => { + let space = match space { + lightcraft_raw::PreviewColorSpace::Srgb => lightcraft_codecs::NamedSpace::Srgb, + lightcraft_raw::PreviewColorSpace::AdobeRgb => lightcraft_codecs::NamedSpace::AdobeRgb, + }; + lightcraft_codecs::decode_jpeg_with_fallback(&jpeg, opts, space).ok() + } + None => lightcraft_codecs::decode(&jpeg, opts).ok(), + } +} + /// Oriented size of the embedded preview of a raw file. fn embedded_preview_size(bytes: &[u8]) -> Option<(u32, u32)> { let jpeg = lightcraft_raw::embedded_preview(bytes)?; @@ -333,8 +348,7 @@ fn embedded_preview_size(bytes: &[u8]) -> Option<(u32, u32)> { /// The embedded preview of a raw file as a working-space image no larger than `max_edge`, oriented. pub fn load_embedded_preview(bytes: &[u8], max_edge: usize) -> Option<(Rgb32f, SourceInfo)> { - let jpeg = lightcraft_raw::embedded_preview(bytes)?; - let d = lightcraft_codecs::decode(&jpeg, lightcraft_codecs::DecodeOptions::fit(max_edge as u32, max_edge as u32)).ok()?; + let d = decode_raw_preview(bytes, lightcraft_codecs::DecodeOptions::fit(max_edge as u32, max_edge as u32))?; let img = d.to_working(); let img = if img.width.max(img.height) > max_edge { fit(&img, max_edge, max_edge, Filter::Mitchell) } else { img }; Some((img.oriented(preview_orientation(bytes, d.orientation)), SourceInfo::default())) @@ -343,8 +357,7 @@ pub fn load_embedded_preview(bytes: &[u8], max_edge: usize) -> Option<(Rgb32f, S /// The embedded preview of a raw file for display (sRGB, oriented, no larger than `max_edge`): the /// loupe and grid show it until the raw itself has been developed ([`crate::media::QuickJob`]). pub fn embedded_preview_srgb(bytes: &[u8], max_edge: usize) -> Option { - let jpeg = lightcraft_raw::embedded_preview(bytes)?; - let mut d = lightcraft_codecs::decode(&jpeg, lightcraft_codecs::DecodeOptions::fit(max_edge as u32, max_edge as u32)).ok()?; + let mut d = decode_raw_preview(bytes, lightcraft_codecs::DecodeOptions::fit(max_edge as u32, max_edge as u32))?; if d.image.width.max(d.image.height) > max_edge { d.image = fit(&d.image, max_edge, max_edge, Filter::Box); d.alpha = None; diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a2..7aafbf349 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; diff --git a/crates/raw/src/lib.rs b/crates/raw/src/lib.rs index f67649de8..e53550aea 100644 --- a/crates/raw/src/lib.rs +++ b/crates/raw/src/lib.rs @@ -43,7 +43,7 @@ pub use lightcraft_geom::Orientation; pub use lightcraft_meta::Metadata; pub use lightcraft_raster::Rgb32f; pub use opcodes::{Opcode, OpcodeLists}; -pub use preview::embedded_preview; +pub use preview::{PreviewColorSpace, embedded_preview, embedded_preview_color_space}; use lightcraft_color::Xy; use lightcraft_tiff::{Tiff, TiffError}; diff --git a/crates/raw/src/preview.rs b/crates/raw/src/preview.rs index ded35d294..42b5d70be 100644 --- a/crates/raw/src/preview.rs +++ b/crates/raw/src/preview.rs @@ -5,6 +5,30 @@ use lightcraft_tiff::image::chunk_bytes; use lightcraft_tiff::tags as t; use lightcraft_tiff::{Ifd, Tiff, makernote}; +/// Colour space of an embedded JPEG when the enclosing raw supplies it instead of the JPEG. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PreviewColorSpace { + Srgb, + AdobeRgb, +} + +/// Nikon maker-note ColorSpace (0x001e: 1 = sRGB, 2 = Adobe RGB), per Nikon tag documentation. +/// This is a fallback only: a JPEG's own ICC/EXIF colour declaration takes precedence. +pub fn embedded_preview_color_space(bytes: &[u8]) -> Option { + let tiff = Tiff::parse(bytes).ok()?; + let make = tiff.ifds.first()?.string(t::MAKE)?; + if !make.to_ascii_uppercase().starts_with("NIKON") { + return None; + } + let e = tiff.exif()?.get(t::MAKER_NOTE)?; + let mn = makernote::parse_makernote(bytes, e.offset, e.count() as u64, tiff.order, &make)?; + match mn.ifd.u16(0x001e)? { + 1 => Some(PreviewColorSpace::Srgb), + 2 => Some(PreviewColorSpace::AdobeRgb), + _ => None, + } +} + /// Whether `b` looks like a displayable (DCT) JPEG: SOI, and the first SOF marker is not lossless. fn is_dct_jpeg(b: &[u8]) -> bool { if b.len() < 4 || b[0] != 0xff || b[1] != 0xd8 { diff --git a/crates/raw/src/vendor/nef.rs b/crates/raw/src/vendor/nef.rs index bf386bb4b..8d1898e0f 100644 --- a/crates/raw/src/vendor/nef.rs +++ b/crates/raw/src/vendor/nef.rs @@ -17,6 +17,19 @@ use lightcraft_tiff::{ByteOrder, Ifd, Tiff, makernote}; const WB_RB_LEVELS: u16 = 0x000c; const BLACK_LEVEL: u16 = 0x003d; const LINEARIZATION_TABLE: u16 = 0x0096; +const CROP_AREA: u16 = 0x0045; + +/// Nikon CropArea is `[left, top, width, height]` in sensor pixels (maker-note tag documentation). +/// Keep the active area/CFA origin unchanged: the default crop is applied after demosaicing. +fn default_crop(mn: Option<&makernote::MakerNote>, w: usize, h: usize) -> Rect { + let fallback = Rect::new(0, 0, w, h); + let Some(values) = mn.and_then(|m| m.ifd.u64s(CROP_AREA)) else { return fallback }; + let [x, y, width, height] = values.as_slice() else { return fallback }; + if *width == 0 || *height == 0 || x.checked_add(*width).is_none_or(|v| v > w as u64) || y.checked_add(*height).is_none_or(|v| v > h as u64) { + return fallback; + } + Rect::new(*x as usize, *y as usize, *width as usize, *height as usize) +} fn raw_ifd(tiff: &Tiff) -> Option<&Ifd> { tiff.all_ifds() @@ -80,9 +93,10 @@ pub(crate) fn decode(bytes: &[u8]) -> Result { }; let white = white_from_data(samples, bits); let active_w = trailing_masked_columns(samples, w, h, white); + let crop = default_crop(mn.as_ref(), active_w, h); let mut metadata = lightcraft_meta::from_tiff(&tiff); - metadata.width = Some(active_w as u32); - metadata.height = Some(h as u32); + metadata.width = Some(crop.width as u32); + metadata.height = Some(crop.height as u32); let img = RawImage { format: RawFormat::Nef, width: w, @@ -94,7 +108,7 @@ pub(crate) fn decode(bytes: &[u8]) -> Result { black, white: vec![white], active_area: Rect::new(0, 0, active_w, h), - crop: Rect::new(0, 0, active_w, h), + crop, orientation: Orientation::from_exif(ifd0.u16(t::ORIENTATION).unwrap_or(1)), color: ColorData::default(), wb_multipliers: wb, @@ -181,6 +195,42 @@ mod tests { TiffWriter::new(ByteOrder::Big, false).write(&[ifd0]).unwrap() } + #[test] + fn preview_color_space_is_read_from_the_nikon_note() { + for (value, expected) in [(1, Some(crate::PreviewColorSpace::Srgb)), (2, Some(crate::PreviewColorSpace::AdobeRgb)), (3, None)] { + let mut mn = IfdBuilder::new(); + mn.set(0x001e, Value::Short(vec![value])); + let bytes = nef_with_note(1, 14, vec![vec![0; 128]], 8, 8, 8, Some(mn)); + assert_eq!(crate::embedded_preview_color_space(&bytes), expected); + } + assert_eq!(crate::embedded_preview_color_space(&nef(1, 14, vec![vec![0; 128]], 8, 8, 8)), None); + assert_eq!(crate::embedded_preview_color_space(b"not a TIFF"), None); + } + + #[test] + fn nikon_crop_area_preserves_sensor_origin_and_rejects_invalid_rectangles() { + let (w, h) = (32, 24); + let words: Vec = (0..w * h).flat_map(|i| (100 + i as u16).to_be_bytes()).collect(); + for (values, expected) in [ + (vec![2, 4, 28, 16], Rect::new(2, 4, 28, 16)), + (vec![31, 4, 28, 16], Rect::new(0, 0, 32, 24)), + (vec![2, 4, 0, 16], Rect::new(0, 0, 32, 24)), + (vec![u32::MAX, 4, 28, 16], Rect::new(0, 0, 32, 24)), + (vec![2, 4, 28], Rect::new(0, 0, 32, 24)), + ] { + let mut mn = IfdBuilder::new(); + mn.set(CROP_AREA, Value::Long(values)); + let bytes = nef_with_note(1, 14, vec![words.clone()], w, h, h, Some(mn)); + let raw = crate::decode(&bytes).unwrap(); + assert_eq!(raw.crop, expected); + assert_eq!(raw.active_area, Rect::new(0, 0, 32, 24)); + assert_eq!(raw.metadata.width, Some(expected.width as u32)); + assert_eq!(raw.metadata.height, Some(expected.height as u32)); + let developed = raw.develop(crate::Method::Bilinear).unwrap(); + assert_eq!((developed.width, developed.height), (expected.width, expected.height)); + } + } + #[test] fn black_level_tag_is_in_14_bit_units() { let (w, h) = (16usize, 4usize); diff --git a/docs/camera-preview-colour.md b/docs/camera-preview-colour.md index 308287c6c..6224547c7 100644 --- a/docs/camera-preview-colour.md +++ b/docs/camera-preview-colour.md @@ -54,3 +54,12 @@ One photo can show too little of a colour for its own fit to learn it: in a seco Profiles are JSON files `.json` in `$LIGHTCRAFT_CAMERA_PROFILES`, else `/camera-profiles` (macOS `~/Library/Application Support/LightCraft/camera-profiles`); a local profile replaces a built-in one. Built-in profiles live in `assets/camera-profiles/` (listed in `assets/ATTRIBUTION.md`) and are compiled in, so the app, CLI and web build share them: ILCE-7M4, fitted to 597 photos (2.2 million colour pairs) shot in 2026, mostly with the Standard creative style and DRO Auto. They hold aggregate colour statistics only. A photo of a profiled model takes its colour from the profile and fits only its own tone and chroma curves (DRO and picture styles vary per shot); the acceptance gates still apply, and a rejected fit falls back as before. Files are read once per process and validated (version, bounded invertible matrix, table shape and finite data); a damaged file is ignored with a warning. The profiles folder's contents are part of the render cache keys, so thumbnails rendered before a profile existed are redone; smart previews built before keep their colour until rebuilt. + + +## Nikon crop and preview colour metadata + +Nikon maker-note `CropArea` (0x0045) supplies the default `[left, top, width, height]` crop. The decoder validates the rectangle against the active sensor area and falls back to that area for missing or invalid values. The CFA origin is unchanged; cropping follows demosaicing. + +An embedded JPEG with neither ICC nor EXIF metadata uses Nikon maker-note `ColorSpace` (0x001e: 1 = sRGB, 2 = Adobe RGB). This fallback is applied before transfer decoding and resizing, and used by quick previews, preview-only RAW and the existing camera-look reference decode. A JPEG's own metadata takes precedence. No fitting algorithm, training split, highlight reconstruction or white-balance control changes are included. Render cache version 12 invalidates older previews. + +Synthetic tests cover crop bounds/CFA preservation, colour-space tags, fallback transfer decoding and metadata precedence. A private Nikon Z 8 sample reports an 8256 × 5504 default crop from an 8280 × 5520 sensor. No private sample or Adobe asset is included. diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9c..dfa45a530 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -59,7 +59,7 @@ Ordered by user impact, then tier, then effort. The checklist above counts featu that decide whether a photographer can switch (see the honest assessment in [ROADMAP.md](../ROADMAP.md#where-we-stand)). Take the first one nobody is working on. -1. **LR-PROF-CAMERACOLOR** (P0): our own camera colour calibration. Sony ARW, Nikon NEF and Panasonic RW2 now get a guarded file-local fit to their own embedded JPEG (colour matrix + hue/saturation/value table + tone and chroma curves, relative WB; per-model profiles pooled from many photos via `lightcraft-cli calibrate` for ARW and NEF; ARW: 7 of 10 public samples accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples from 6 bodies accepted, ΔE 13–46 → 3–8, one mixed-light scene 13 → 12; RW2: 140 of 174 public samples accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0); measured calibration and fidelity remain missing. Other non-DNG raws and rejected fits still use a neutral matrix. Expand validated preview fitting and use matrices the files carry themselves; never Adobe data. +1. **LR-PROF-CAMERACOLOR** (P0): our own camera colour calibration. Sony ARW, Nikon NEF and Panasonic RW2 now get a guarded file-local fit to their own embedded JPEG (colour matrix + hue/saturation/value table + tone and chroma curves, relative WB; per-model profiles pooled from many photos via `lightcraft-cli calibrate` for ARW and NEF; ARW: 7 of 10 public samples accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples from 6 bodies accepted, ΔE 13–46 → 3–8, one mixed-light scene 13 → 12; RW2: 140 of 174 public samples accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0); Nikon CropArea and untagged-preview ColorSpace metadata are respected; measured calibration and fidelity remain missing. Other non-DNG raws and rejected fits still use a neutral matrix. Expand validated preview fitting and use matrices the files carry themselves; never Adobe data. 2. **LR-IMP-FORMATS** (P0): **CR3** first (every Canon body since ~2018), then compressed RAF / ORF, Nikon "lossy after split" NEF, Canon sRAW; HEIC/AVIF decode. Clean-room, from prose descriptions only (see `crates/raw/src/vendor/nefc.rs` for how compressed NEF was done). Until decoded, such photos are `preview_only`. @@ -125,7 +125,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-IMP-LOCAL | Work on files in place | P0 | ✅ | `cmd:library.browse`, `cmd:photo.addToLibrary`, `cmd:library.import` (mode add), `cmd:local.addRoot`, `cmd:local.hide`, `cmd:local.restoreHidden`, `crates/engine/src/cmd/browse.rs`, `crates/ui-egui/src/panels/left.rs` (`local_section`), `crates/ui-egui/src/panels/grid.rs` (`folder_header`) | Local: browse Pictures / Desktop / Downloads / Home or any folder without adding it (breadcrumb, Include subfolders, Add N to My Photos); browsed photos stay out of All Photos, albums and counts; folders picked with Browse Folder… (or Keep in Local / Add to Local on a folder row) stay listed across restarts, and browsing a folder inside a listed one highlights it inside that tree (opened down to it, siblings reachable) instead of adding a root; edits go to XMP sidecars; importing promotes them; right-click a top-level location → Remove from Local hides the shortcut only (nothing on disk or in the catalog changes), saved with the UI state, and “Show N hidden locations” (or Browse Folder… on it) brings it back; a folder is one location however its path is spelled (`/` or `\`, trailing separator, `.`/`..`, drive-letter case; `folder_key` in `crates/catalog/src/query.rs`); browsed photos nobody changed are forgotten once their folder has not been browsed for 30 days (Settings → Performance → Local folders, 0 = never; checked when the library opens, or `cmd:library.forgetLocal` with `dryRun`; files and sidecars stay, browsing again brings them back; see `crates/catalog/src/local.rs` for what counts as a change) | | LR-IMP-SIDECAR-SPLIT | Separate XMP sidecar variants | P2 | ⬜ | `cmd:library.xmpPreferences` | sidecar naming option exists (stem/full), no split sidecars | | LR-IMP-FORMATS | Supported formats | P0 | 🟡 | `crates/codecs/src/lib.rs`, `crates/raw/src/lib.rs` | JPEG, PNG, TIFF, WebP, JXL, PSD, GIF, BMP; DNG (incl. lossy / Smart Preview DNG), CR2 (colour-filter layout from the file's `CR2CFAPattern` tag, issue #85), ARW (pre-2017 bodies: white balance and black level from enciphered maker-note / `SR2SubIFD` data, issue #148; downsized Sony lossless YCbCr 4:2:0 / 4:2:2 tiles decoded to linear RGB, verified on private ILCE-7M4 3:2 & 4:3 examples including 16-bit full-resolution export), NEF (uncompressed, lossless and lossy compressed 12/14-bit, `crates/raw/src/vendor/nefc.rs`), RAF, RW2 / Leica RWL / Panasonic RAW in every raw format (compressed 4 and 6, the prefix-coded strips of format 8 from the GH6 on, packed 2/5/7, the 16-bit words of the 2005–2007 bodies; black levels, in-camera aspect crops and mapped-out defect pixels; 178 CC0 files from 118 bodies checked, `crates/raw/src/vendor/rw2.rs`), PEF, ORF. 12-bit NEF black level: maker note `0x003d` is in 14-bit units (D750, D780, D850, D7500, Z 50 checked). CR3 opens from its full-size embedded JPEG with full metadata (Exif, GPS, XMP from the `CMT1`/`CMT2`/`CMT4` boxes, `crates/meta/src/cr3.rs`) but its raw data is not decoded yet. Missing: CR3 raw decoding (CRX), compressed RAF/ORF, NEFs labelled compressed (34713) but stored uncompressed without a `0x0096` table (Z 6 packed 14-bit, D850 12-bit uncompressed), NEF "lossy after split" (preview only: imported with `preview_only` = the decoder's reason, rendered as a rendered JPEG, flagged by a grid badge and loupe/Edit/Info notices, `previewOnly` in `catalog.query`; Reload clears it once the file decodes), HEIC/AVIF decode | -| LR-IMP-CAMERA-COVERAGE | Camera coverage, verified per model | P0 | 🟡 | `crates/raw/tests/corpus.rs`, `xtask/src/main.rs` | a supported container is not the same as every camera that writes it decoding correctly: ~55 CC0 corpus files (plus ~60 NEFs checked by hand and 178 Panasonic / Leica RW2, RWL and RAW files from 118 bodies, checked against each file's own JPEG) against the >1,000 models Lightroom lists; per-model differences still surface (e.g. the CR2 colour-filter layout differed by model until it was read from the file's own tag, issue #85, checked on 35 CR2 bodies; pre-2017 Sony ARWs opened green until their scrambled white balance / black level were read, issue #148, checked on 16 ARWs from 15 Sony bodies). Grow the CC0 corpus per model and test each sample decodes with plausible colour | +| LR-IMP-CAMERA-COVERAGE | Camera coverage, verified per model | P0 | 🟡 | `crates/raw/tests/corpus.rs`, `xtask/src/main.rs` | a supported container is not the same as every camera that writes it decoding correctly: ~55 CC0 corpus files (plus ~60 NEFs checked by hand and 178 Panasonic / Leica RW2, RWL and RAW files from 118 bodies, checked against each file's own JPEG) against the >1,000 models Lightroom lists; per-model differences still surface (e.g. the CR2 colour-filter layout differed by model until it was read from the file's own tag, issue #85, checked on 35 CR2 bodies; pre-2017 Sony ARWs opened green until their scrambled white balance / black level were read, issue #148, checked on 16 ARWs from 15 Sony bodies). Grow the CC0 corpus per model and test each sample decodes with plausible colour. Nikon CropArea and untagged-preview colour-space regression tests; private Z 8 crop verified. | | LR-IMP-CULL-AT-IMPORT | Culling analysis at import | P2 | 🟡 | `cmd:photo.analyze` | run Assisted Culling on the imported photos (they're selected after an import); not automatic | | LR-IMP-MOVE | Move on import [Classic] | P1 | ✅ | `cmd:library.import` (`mode` move), `crates/engine/src/import_move.rs`, `crates/engine/src/tests_import_move.rs`, `crates/ui-egui/src/import.rs` | Transfer ▸ Move with Copy's destination, folders (day / month / one folder / custom template), rename template and example destination; XMP sidecars (both namings) move along; each source is removed only after its destination is written (hard link on the same volume, else copied, synced and compared byte for byte) and its catalog record is saved; failed, duplicate, unchecked files keep their sources, taken names get -1, -2…, never overwritten; files already in the destination/library are added in place; sources that can't be removed (read-only card) are kept and reported (`kept`). Undo removes the photos from the library but leaves the moved files at the destination. No Copy as DNG while moving | | LR-IMP-DNG-CONVERT | Convert to DNG on import [Classic] | P2 | ✅ | `cmd:library.import` (`dng`), `crates/ui-egui/src/import.rs` | copy imports: Raw files ▸ Copy as DNG (lossless; the card is untouched); the raw copy is removed only once its DNG is verified and on disk (issue #106) | From d614dc72bdb8554b554f8e7821c0c3c3e3a1e57c Mon Sep 17 00:00:00 2001 From: Hanmo Date: Thu, 8 Oct 2026 10:26:18 +0800 Subject: [PATCH 004/213] M3: isolate global Tint direction correction for compatibility review --- crates/color/src/cct.rs | 15 +++++++++------ crates/develop/src/settings.rs | 1 + crates/engine/src/media.rs | 2 +- crates/gpu/tests/equivalence.rs | 21 +++++++++++++++++++++ crates/pipeline/src/auto.rs | 25 +++++++++++++++++++++++++ crates/pipeline/src/tests.rs | 25 +++++++++++++++++++++++++ docs/parity.md | 10 +++++----- docs/tint-direction.md | 33 +++++++++++++++++++++++++++++++++ 8 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 docs/tint-direction.md diff --git a/crates/color/src/cct.rs b/crates/color/src/cct.rs index abd241dde..941e8a4f2 100644 --- a/crates/color/src/cct.rs +++ b/crates/color/src/cct.rs @@ -2,7 +2,9 @@ //! //! The Planckian locus uses the Kim et al. (2002) cubic approximation (1667–25000 K); outside that //! range it is extended linearly in mired space. Tint is a signed offset perpendicular to the locus in -//! CIE 1960 (u, v): **positive tint = magenta** (below the locus), `Duv = −tint / TINT_SCALE`. +//! CIE 1960 (u, v). Tint describes the **correction**: positive adds magenta, negative adds green. +//! A positive correction neutralises a green illuminant above the locus, so its source white uses +//! `Duv = tint / TINT_SCALE`. Bradford adaptation removes that source cast. use crate::{Mat3, RgbSpace, Xy, bradford}; @@ -65,11 +67,12 @@ fn locus_normal(t: f64) -> (f64, f64) { if ny < 0.0 { (-nx, -ny) } else { (nx, ny) } } -/// Chromaticity of the white point for temperature `t` (K) and `tint`. +/// Source illuminant chromaticity for temperature `t` (K) and correction `tint`. +/// Positive tint means a green source white, whose correction adds magenta to the image. pub fn temp_tint_to_xy(t: f64, tint: f64) -> Xy { let (u, v) = locus_uv(t); let (nu, nv) = locus_normal(t); - let duv = -tint / TINT_SCALE; + let duv = tint / TINT_SCALE; uv_to_xy(u + nu * duv, v + nv * duv) } @@ -104,7 +107,7 @@ pub fn xy_to_temp_tint(p: Xy) -> (f64, f64) { let (lu, lv) = locus_uv(t); let (nu, nv) = locus_normal(t); let duv = (u - lu) * nu + (v - lv) * nv; - (t, -duv * TINT_SCALE) + (t, duv * TINT_SCALE) } /// Matrix (linear RGB in `space` → same space) that white-balances a scene lit by `src` so that it @@ -141,10 +144,10 @@ mod tests { let warm = temp_tint_to_xy(3000.0, 0.0); let cool = temp_tint_to_xy(9000.0, 0.0); assert!(warm.x > cool.x); - // positive tint is magenta: lower v + // A positive magenta correction specifies a greener source illuminant: higher v. let (_, v0) = xy_to_uv(temp_tint_to_xy(5000.0, 0.0)); let (_, v1) = xy_to_uv(temp_tint_to_xy(5000.0, 50.0)); - assert!(v1 < v0); + assert!(v1 > v0); } #[test] diff --git a/crates/develop/src/settings.rs b/crates/develop/src/settings.rs index 800f229b5..3297b462c 100644 --- a/crates/develop/src/settings.rs +++ b/crates/develop/src/settings.rs @@ -158,6 +158,7 @@ impl WbMode { pub struct WhiteBalance { pub mode: WbMode, pub temp: f64, + /// Correction direction: negative adds green, positive adds magenta. pub tint: f64, } diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a2..7aafbf349 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; diff --git a/crates/gpu/tests/equivalence.rs b/crates/gpu/tests/equivalence.rs index c16b46cb4..00cac8bca 100644 --- a/crates/gpu/tests/equivalence.rs +++ b/crates/gpu/tests/equivalence.rs @@ -639,3 +639,24 @@ fn output_spaces_match() { } } } + +#[test] +fn tint_directions_match_cpu_for_raw_and_rendered_sources() { + if !gpu() { + return; + } + let src = Arc::new(Rgb32f::filled(64, 64, [0.18; 3])); + for info in [ + SourceInfo::default(), + SourceInfo { raw: true, relative_wb: true, ..Default::default() }, + SourceInfo { raw: true, as_shot_temp: 4200.0, as_shot_tint: 15.0, ..Default::default() }, + ] { + for delta in [-50.0, 50.0] { + let mut s = DevelopSettings::default(); + s.wb.mode = WbMode::Custom; + s.wb.temp = info.as_shot_temp; + s.wb.tint = info.as_shot_tint + delta; + check("tint direction", &src, &info, &s, &RenderRequest::fit(64, 64)); + } + } +} diff --git a/crates/pipeline/src/auto.rs b/crates/pipeline/src/auto.rs index c34d33110..45100a1ea 100644 --- a/crates/pipeline/src/auto.rs +++ b/crates/pipeline/src/auto.rs @@ -191,3 +191,28 @@ mod bw_tests { assert_eq!(auto_bw_mix(&grey, &SourceInfo::default(), &DevelopSettings::default()), [0.0; 8]); } } + +#[cfg(test)] +mod tint_tests { + use super::*; + + #[test] + fn auto_wb_and_picker_correct_green_with_positive_tint() { + // The picker delegates a sampled patch to this same auto_wb implementation. + for (rgb, sign) in [([0.18, 0.24, 0.18], 1.0), ([0.24, 0.18, 0.24], -1.0)] { + let img = Rgb32f::filled(16, 16, rgb); + let info = SourceInfo { raw: true, relative_wb: true, ..Default::default() }; + let (temp, tint) = auto_wb(&img, &info); + assert!(tint * sign > 0.0, "{rgb:?}: temp {temp}, tint {tint}"); + let mut s = DevelopSettings::default(); + s.wb.mode = lightcraft_develop::WbMode::Custom; + s.wb.temp = temp; + s.wb.tint = tint; + let mut corrected = img; + crate::local::white_balance(&mut corrected, &info, &s); + let p = corrected.get(0, 0); + let spread = p[0].max(p[1]).max(p[2]) - p[0].min(p[1]).min(p[2]); + assert!(spread < 0.003, "{rgb:?} -> {p:?}"); + } + } +} diff --git a/crates/pipeline/src/tests.rs b/crates/pipeline/src/tests.rs index 61e0de7c1..4bfa8c19a 100644 --- a/crates/pipeline/src/tests.rs +++ b/crates/pipeline/src/tests.rs @@ -330,3 +330,28 @@ fn soft_proof_maps_into_the_proof_gamut_and_flags_what_does_not_fit() { assert!(blue(&pro) > 0); assert!(red(&pro) < n, "ProPhoto holds more than sRGB"); } + +#[test] +fn tint_negative_is_green_and_positive_is_magenta() { + use lightcraft_develop::WbMode; + let src = Rgb32f::filled(16, 16, [0.18; 3]); + // Rendered images, uncalibrated RAW and calibrated RAW with a nonzero As Shot tint. + for info in [ + SourceInfo::default(), + SourceInfo { raw: true, relative_wb: true, ..Default::default() }, + SourceInfo { raw: true, as_shot_temp: 4200.0, as_shot_tint: 15.0, ..Default::default() }, + ] { + let mut s = DevelopSettings::default(); + let neutral = render(&src, &info, &s, &RenderRequest::fit(16, 16)).image.get(8, 8); + assert!((neutral[0] as i16 - neutral[1] as i16).abs() <= 1); + assert!((neutral[2] as i16 - neutral[1] as i16).abs() <= 1); + s.wb.mode = WbMode::Custom; + s.wb.temp = info.as_shot_temp; + for delta in [-50.0, 50.0] { + s.wb.tint = info.as_shot_tint + delta; + let p = render(&src, &info, &s, &RenderRequest::fit(16, 16)).image.get(8, 8); + let magenta = (p[0] as f64 + p[2] as f64) / 2.0 - p[1] as f64; + assert!(magenta * delta > 100.0, "delta {delta} must follow the green/magenta track, got {p:?}"); + } + } +} diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9c..3ed8afd7d 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -26,7 +26,7 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table | C. Views & navigation (VIEW) | 16 | 0 | 1 | 0 | 9/9 (100%) | 4/4 (100%) | | D. Search & filter (FILT) | 11 | 1 | 1 | 0 | 4/4 (100%) | 4/4 (100%) | | E. Metadata (META) | 5 | 1 | 0 | 0 | 2/2 (100%) | 2/2 (100%) | -| F. Edit panel — global adjustments (EDIT) | 42 | 1 | 5 | 1 | 28/28 (100%) | 13/14 (93%) | +| F. Edit panel — global adjustments (EDIT) | 41 | 2 | 5 | 1 | 27/28 (96%) | 13/14 (93%) | | G. Profiles (PROF) | 6 | 2 | 3 | 0 | 3/4 (75%) | 2/3 (67%) | | H. Crop & rotate (CROP) | 9 | 0 | 0 | 1 | 6/6 (100%) | 3/3 (100%) | | I. Remove / healing (REM) | 7 | 1 | 2 | 2 | 4/4 (100%) | 2/3 (67%) | @@ -48,9 +48,9 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table | Y. Menus | 81 | 0 | 4 | 8 | 47/47 (100%) | 23/23 (100%) | | Z. Keyboard shortcuts (desktop) | 74 | 3 | 3 | 1 | 49/52 (94%) | 22/23 (96%) | | Lightroom Classic extras | 31 | 10 | 39 | 9 | — | 21/22 (95%) | -| **Total** | 389 | 34 | 86 | 37 | 194/200 (97%) | 139/149 (93%) | +| **Total** | 388 | 35 | 86 | 37 | 193/200 (97%) | 139/149 (93%) | -Weighted completion (✅ = 1, 🟡 = ½, 🚫 left out): **79.8%** of 509 in-scope rows — P0 98.5% of 200 · P1 95.6% of 149 · P2 41.6% of 160. +Weighted completion (✅ = 1, 🟡 = ½, 🚫 left out): **79.7%** of 509 in-scope rows — P0 98.2% of 200 · P1 95.6% of 149 · P2 41.6% of 160. ## Top gaps @@ -66,7 +66,7 @@ Take the first one nobody is working on. 3. **LR-IMP-CAMERA-COVERAGE** (P0): per-model verification; grow the CC0 corpus and fix per-model bugs (like the CR2 colour-filter layout, fixed in #85 by reading the file's own tag). 4. **LR-BEHAV-RENDER-FIDELITY** (P1): a side-by-side fidelity suite against Lightroom renders (kept local in `plan/`), - then tune tone, highlights, texture/clarity/dehaze, NR and sharpening against it. + then tune tone, highlights, texture/clarity/dehaze, NR and sharpening against it. Global Tint direction has isolated regression coverage; review existing-edit compatibility before landing its corrected sign. 5. **LR-EDIT-OPTICS-PROFILE** (P1): a lens-profile database of our own (embedded DNG/maker corrections work today). 6. **AI masks and Enhance** (LR-MASK-SUBJECT / SKY / PEOPLE / OBJECTS, LR-EDIT-DETAIL-DENOISE, SUPERRES, LENSBLUR): Object and Describe masks run SAM 3 in pure Rust (`crates/segment`); the weights (SAM License) are never bundled: @@ -234,7 +234,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-EDIT-COLOR-WB-PRESET | White-balance presets | P0 | ✅ | `cmd:develop.wb` | | | LR-EDIT-COLOR-WB-PICKER | White-balance eyedropper | P0 | ✅ | `cmd:tool.wbPicker`, `cmd:develop.wbPick` | no magnified loupe while picking | | LR-EDIT-COLOR-TEMP | Temperature | P0 | ✅ | `ctl:wb.temp` | relative scale for non-raw in the UI | -| LR-EDIT-COLOR-TINT | Tint | P0 | ✅ | `ctl:wb.tint` | | +| LR-EDIT-COLOR-TINT | Tint | P0 | 🟡 | `ctl:wb.tint`, `docs/tint-direction.md` | Negative/left adds green and positive/right magenta; CPU/GPU regression tests. Existing-edit/preset/XMP migration policy needs review before landing | | LR-EDIT-COLOR-VIBRANCE | Vibrance | P0 | ✅ | `ctl:color.vibrance` | | | LR-EDIT-COLOR-SATURATION | Saturation | P0 | ✅ | `ctl:color.saturation` | | | LR-EDIT-COLOR-MIXER-HSL | 8-band colour mixer | P0 | ✅ | `ctl:mixer.*` | no targeted (drag-on-image) mode | diff --git a/docs/tint-direction.md b/docs/tint-direction.md new file mode 100644 index 000000000..e01b52a91 --- /dev/null +++ b/docs/tint-direction.md @@ -0,0 +1,33 @@ +# Global Tint direction: proposed correction and compatibility + +This change is under review. Negative/left Tint should add green; positive/right should add magenta. It is a global white-balance change, not a Nikon-specific decoder fix. + +## Reproducible evidence + +The existing UI already declares `Track::Tint` as green → magenta (`crates/develop/src/controls.rs`) and paints a green left endpoint and purple right endpoint (`crates/ui-egui/src/widgets.rs`). Local mask tint also implements the correction direction: positive tint reduces green (`crates/pipeline/src/finish.rs`). The old global conversion contradicts both. + +The synthetic `tint_negative_is_green_and_positive_is_magenta` regression renders an 18% neutral patch. Before the fix, setting global Tint to -50 produces sRGB `[129, 113, 128]`: red and blue exceed green, so the result is magenta. The test asserts the output direction for rendered sources, relative-WB RAW, and calibrated RAW with a nonzero As Shot reference. The complementary auto/picker test checks that correcting a green patch returns positive Tint and neutralises the patch. + +Run the evidence without private samples: + +```sh +cargo test -p lightcraft-pipeline tint_ -- --nocapture +cargo test -p lightcraft-gpu --test equivalence tint_directions -- --nocapture +cargo test -p lightcraft-color cct +``` + +For an external behaviour reference, see Adobe's Lightroom Classic documentation, [Adjust image color and tone](https://helpx.adobe.com/lightroom-classic/help/tone-control-adjustment.html), in the white-balance/Tint section. The automated documentation fetch returned HTTP 403 during this split, so this PR's directly verified evidence is the existing UI convention and synthetic rendered pixels above; it does not claim a new observed Lightroom comparison or copy any Adobe asset/profile. + +## Why the sign changes + +`temp_tint_to_xy` describes the **source illuminant** removed by Bradford adaptation. To add magenta to the output, the source white must be greener (positive Duv), not more magenta. Therefore the correction convention requires `Duv = +tint / TINT_SCALE`, and the inverse conversion must return `tint = +Duv * TINT_SCALE`. + +The CPU and GPU consume the same `wb_matrix_for` result. RAW white-point inference, auto WB and the picker use the inverse conversion. Both directions change together; local mask tint and calibration shadow tint are unchanged. The render-cache version increases to invalidate stale output. + +## Existing edits: review required + +This changes the rendering meaning of saved nonzero global Tint values, including catalog edits, presets, history and imported XMP. No values are silently rewritten and no automatic migration is included. An old Auto WB result stored as numeric settings is also an existing edit; rerunning Auto computes values in the new convention. + +As Shot continues to apply an identity adjustment. For a source whose As Shot tint is zero, negating the previous custom Tint reproduces its previous appearance. This is not a universal migration: calibrated RAW can retain a nonzero, old-sign reference tint in catalog metadata, cached source information or smart previews. Those references and their provenance need consideration alongside the edit. Existing XMP may originate from another application using the intended correction convention, so indiscriminately negating all imported values is also wrong. + +Before merging, decide whether to version/migrate native saved edits and reference metadata, preserve a legacy rendering mode, or accept an explicitly documented rendering change. Keep this PR a draft until that policy is reviewed. If another render-cache change lands first, rebase and increment the cache version again. From fc4b054fa43b8ee637be0a9f50ab5f73fa32da7a Mon Sep 17 00:00:00 2001 From: radialmonster Date: Wed, 7 Oct 2026 22:52:45 -0400 Subject: [PATCH 005/213] LR-IMP-FORMATS: RW2 format 8: invalidate cached thumbnails The decoder change alters format-8 renders while the photo's content hash and settings stay the same, so thumbnails rendered before it (edited photos keep theirs on disk) would survive. Bump RENDER_CACHE_VERSION. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/media.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a2..7aafbf349 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; From 6a34a1f8b85291697c659a26726682d687bdad1c Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 06:56:08 +0400 Subject: [PATCH 006/213] Add files for further editing --- crates/ui-egui/locales/ru-formats.json | 122 +++ crates/ui-egui/locales/ru.json | 1263 ++++++++++++++++++++++++ 2 files changed, 1385 insertions(+) create mode 100644 crates/ui-egui/locales/ru-formats.json create mode 100644 crates/ui-egui/locales/ru.json diff --git a/crates/ui-egui/locales/ru-formats.json b/crates/ui-egui/locales/ru-formats.json new file mode 100644 index 000000000..9e2c96dd3 --- /dev/null +++ b/crates/ui-egui/locales/ru-formats.json @@ -0,0 +1,122 @@ +{ + " ({} in Recently Deleted)": " ({} в «Недавно удалённых»)", + " · {skipped} skipped (file exists)": " · пропущено: {skipped} (файл существует)", + " · {} duplicate{} skipped": " · пропущено дубликатов: {}{:.0}", + " · {} failed: {}": " · не удалось: {}; ошибка: {}", + " · {} moved": " · перемещено: {}", + " · {} not readable": " · не читается: {}", + ", {failed} file{} not readable": ", не читается файлов: {failed}{:.0}", + "Add {local_n} to My Photos": "Добавить в «Мои фото»: {local_n}", + "Add {} Photo{}": "Добавить фото: {}{:.0}", + "Add “{}” to Favorites": "Добавить «{}» в избранное", + "Added {n} photo{} to My Photos": "В «Мои фото» добавлено фото: {n}{:.0}", + "Added {n} photo{} to “{}”": "В «{1}» добавлено фото: {n}{0:.0}", + "Adding photos… {} of {}": "Добавление фото… {} из {}", + "All {n} selected photos move by the same amount.": "Все выбранные фото ({n}) сдвигаются на одно и то же значение.", + "Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "Уже в библиотеке, в «Недавно удалённых»: выбрано фото: {}{:.0} · щёлкните правой кнопкой ▸ Восстановить или Удалить навсегда, чтобы импортировать заново", + "Already in the library: {} photo{} selected in All Photos": "Уже в библиотеке: выбрано фото в разделе «Все фото»: {}{:.0}", + "Auto ({})": "Авто ({})", + "Auto Sync: edits apply to {n} photos": "Автосинхронизация: правки применяются к фото: {n}", + "Automatic ({} MB)": "Автоматически ({} МБ)", + "Build 1:1 Previews ({scope})": "Создать просмотры 1:1 ({scope})", + "Build Smart Previews ({scope})": "Создать смарт-просмотры ({scope})", + "Build Standard-Sized Previews ({scope})": "Создать просмотры стандартного размера ({scope})", + "Click {}:1": "Щелчок {}:1", + "Create {n} Virtual Copies": "Создать виртуальные копии: {n}", + "Decoder: {reason}": "Декодер: {reason}", + "Delete \"{label}\"": "Удалить «{label}»", + "Delete \"{label}\" (and the mask)": "Удалить «{label}» (и маску)", + "Delete {n} Photos": "Удалить фото: {n}", + "Delete “{current}”": "Удалить «{current}»", + "Discard Smart Previews ({scope})": "Удалить смарт-просмотры ({scope})", + "Duplicate \"{label}\"": "Дублировать «{label}»", + "Example: {example}": "Пример: {example}", + "Exported {n} photo{}": "Экспортировано фото: {n}{:.0}", + "Exported {ok} of {total} photo{}": "Экспортировано фото: {ok} из {total}{:.0}", + "Exported {} point curve preset{}": "Экспортировано пресетов точечной кривой: {}{:.0}", + "Exported {} preset{}": "Экспортировано пресетов: {}{:.0}", + "Exporting {} of {total}": "Экспорт: {} из {total}", + "Filter bar — {active} active filter{}": "Панель фильтров — активных фильтров: {active}{:.0}", + "Healed {n} dust spot{}": "Убрано пылинок: {n}{:.0}", + "Import cancelled · {} photo{} added": "Импорт отменён · добавлено фото: {}{:.0}", + "Imported {n} point curve preset{}": "Импортировано пресетов точечной кривой: {n}{:.0}", + "Imported {n} preset{}": "Импортировано пресетов: {n}{:.0}", + "Imported {n} preset{}, {f} file{} not readable": "Импортировано пресетов: {n}{:.0}, не читается файлов: {f}{:.0}", + "Imported {} photo{}": "Импортировано фото: {}{:.0}", + "Label Set: {name}": "Набор меток: {name}", + "Matches: {}": "Совпадения: {}", + "Merging… {stage} {:.0}%": "Слияние… {stage} {:.0}%", + "Move {what} to Recently Deleted?": "Переместить {what} в «Недавно удалённые»?", + "Named ({named_n})": "Именованные ({named_n})", + "Paste into {n} photo{} — only settings that were copied are pasted": "Вставка в фото: {n}{:.0} — вставляются только скопированные настройки", + "Preset: {name}": "Пресет: {name}", + "Preview only — editing the camera's embedded JPEG ({})": "Только просмотр — правка встроенного в камеру JPEG ({})", + "Preview only — {}": "Только просмотр — {}", + "Profile: {}": "Профиль: {}", + "Proof Copy ({})": "Пробная копия ({})", + "Proof Preview · {}": "Пробный просмотр · {}", + "Quick Develop · {n} photos": "Быстрая коррекция · фото: {n}", + "Quick Develop: {name}": "Быстрая коррекция: {name}", + "Rated {}": "Оценка: {}", + "Reading photos… {done} of {total}": "Чтение фото… {done} из {total}", + "Reading photos… {} of {}": "Чтение фото… {} из {}", + "Redo {}": "Повторить {}", + "Remove “{}” from Favorites": "Убрать «{}» из избранного", + "Rename {n} Photos…": "Переименовать фото: {n}…", + "Rename “{name}”": "Переименовать «{name}»", + "Rendering on the CPU: {why}": "Рендеринг на CPU: {why}", + "Reset {name}": "Сбросить: {name}", + "Reset {} Channel": "Сбросить канал: {}", + "Saved export preset “{}”": "Пресет экспорта «{}» сохранён", + "Show {hidden} hidden location{}": "Показать скрытые места: {hidden}{:.0}", + "Showing {n} of {}": "Показано {n} из {}", + "Smart album: {}": "Смарт-альбом: {}", + "Undo {}": "Отменить {}", + "Updated “{name}”": "«{name}» обновлено", + "Version {} — a clean-room, pure-Rust photo library and raw developer.": "Версия {} — независимо разработанная библиотека фото и RAW-конвертер на чистом Rust.", + "Version: {}": "Версия: {}", + "none (CPU rendering): {why}": "нет (рендеринг на CPU): {why}", + "the {n} selected photos": "выбранные {n} фото", + "the {} photos in view": "все {} фото в представлении", + "these {count} photos": "эти {count} фото", + "{count} smart preview{} · {:.1} MB": "Смарт-просмотров: {count}{:.0} · {:.1} МБ", + "{f} folders (and their subfolders)": "Папок: {f} (и их подпапок)", + "{label} Grading": "Цветокоррекция: {label}", + "{label} {d:+} on every selected photo": "{label} {d:+} для каждого выбранного фото", + "{label}, {n} photos": "{label}, фото: {n}", + "{matching} of {t} photos": "Фото: {matching} из {t}", + "{matching} photos": "Фото: {matching}", + "{mode} spot {} of {}": "{mode}: пятно {} из {}", + "{month}": "{month}", + "{month} {year}": "{month:02}.{year}", + "{msg} ({failed} failed)": "{msg} (ошибок: {failed})", + "{name} Label": "Метка: {name}", + "{name} Off": "{name}: выкл.", + "{name} On": "{name}: вкл.", + "{name} — double-click to reset it": "{name} — двойной щелчок для сброса", + "{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.": "Не записано на диск, хранится только в памяти (изменений: {n}): {e}\nLightCraft повторяет попытку автоматически; при выходе сейчас они будут потеряны.{:.0}{:.0}", + "{n} found · {sel} selected": "Найдено: {n} · выбрано: {sel}", + "{n} photos": "Фото: {n}", + "{n} photos in {} are hidden by the filters above": "Фильтры выше скрывают фото в разделе «{}»: {n}", + "{n} photo{}": "Фото: {n}{:.0}", + "{n} photo{} match · updates automatically as photos change": "Подходящих фото: {n}{:.0} · обновляется автоматически при изменении фото", + "{n} photo{} now · updates automatically as photos change": "Сейчас фото: {n}{:.0} · обновляется автоматически при изменении фото", + "{sel_n} selected · {counted}": "Выбрано: {sel_n} · {counted}", + "{stage}… {:.0}%": "{stage}… {:.0}%", + "{verb} {n} Photo{}": "{verb} (фото: {n}){:.0}", + "{weekday}, {day}": "{weekday}, {day}", + "{weekday}, {day} {month} {year}": "{weekday}, {day:02}.{month:02}.{year}", + "{what} merge added": "{what}: слияние добавлено", + "{what} merge failed: {e}": "{what}: ошибка слияния: {e}", + "{year}": "{year}", + "{} and higher": "{} и выше", + "{} files": "Файлов: {}", + "{} files and {f} folder{}": "Файлов: {}, папок: {f}{:.0}", + "{} of 4 guides — drag along lines that should be vertical or horizontal.": "Направляющих: {} из 4 — проведите вдоль линий, которые должны быть вертикальными или горизонтальными.", + "{} photos": "Фото: {}", + "{} photo{} not readable": "Не читается фото: {}{:.0}", + "{} spot(s) on this photo": "Пятен на этом фото: {}", + "· {dups} already in the library": "· уже в библиотеке: {dups}", + "· {n} photo{}": "· фото: {n}{:.0}", + "… and {more} more": "… и ещё {more}" +} diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json new file mode 100644 index 000000000..7948e4c66 --- /dev/null +++ b/crates/ui-egui/locales/ru.json @@ -0,0 +1,1263 @@ +{ + "+ Group": "+ Группа", + "+ Rule": "+ Условие", + "0: nothing is rejected.": "0: ничего не отклоняется.", + "A filter is hiding this view's photos: change it, or clear it (View → Clear Filters)": "Фильтр скрывает фото в этом представлении: измените его или сбросьте (Вид → Сбросить фильтры)", + "A one-time download of about": "Однократная загрузка около", + "About LightCraft": "О программе LightCraft", + "Activity": "Активность", + "Actual size": "Реальный размер", + "Add": "Добавить", + "Add Catchlight": "Добавить блик в глазах", + "Add Folder to Local": "Добавить папку в «Локальное»", + "Add Point Color Sample": "Добавить точечный образец цвета", + "Add Red Eye Correction": "Добавить коррекцию красных глаз", + "Add Remove Spot": "Добавить удаление пятна", + "Add Selected Photos": "Добавить выбранные фото", + "Add a rule (⌥-click: a group)": "Добавить условие (⌥+щелчок: группу)", + "Add in place": "Добавить на месте", + "Add in place: the library references the files where they are; nothing is copied or moved.": "Добавить на месте: библиотека ссылается на файлы там, где они лежат; ничего не копируется и не перемещается.", + "Add keyword": "Добавить ключевое слово", + "Add number": "Добавить номер", + "Add to": "Добавить в", + "Add to Album": "Добавить в альбом", + "Add to Favorites": "Добавить в избранное", + "Add to My Photos": "Добавить в «Мои фото»", + "Add to Selected Photos": "Добавить к выбранным фото", + "Add to Target Album": "Добавить в целевой альбом", + "Add to this mask": "Добавить к этой маске", + "Add/Subtract/Intersect Mask": "Добавить/вычесть/пересечь маску", + "Added to photos you import that don't already have it.": "Добавляется к импортируемым фото, у которых этого ещё нет.", + "Adding": "Добавление", + "Advanced search…": "Расширенный поиск…", + "After": "После", + "After 30 days": "Через 30 дней", + "After 7 days": "Через 7 дней", + "After 90 days": "Через 90 дней", + "After a year": "Через год", + "Album": "Альбом", + "Albums": "Альбомы", + "All": "Все", + "All Metadata": "Все метаданные", + "All Metadata…": "Все метаданные…", + "All Photos": "Все фото", + "All rights reserved": "Все права защищены", + "All types": "Все типы", + "Alt Text": "Альтернативный текст", + "Always": "Всегда", + "Amount": "Количество", + "Aperture": "Диафрагма", + "Application": "Приложение", + "Apply Color Label Set": "Применить набор цветовых меток", + "Apply Filter Preset": "Применить пресет фильтра", + "Apply Metadata Preset": "Применить пресет метаданных", + "Apply Point Curve Preset": "Применить пресет точечной кривой", + "Apply Preset": "Применить пресет", + "Apply Settings JSON": "Применить JSON настроек", + "Approved": "Одобрено", + "Aqua": "Голубой", + "Aqua Hue": "Тон голубого", + "Aqua Luminance": "Яркость голубого", + "Aqua Saturation": "Насыщенность голубого", + "ArtCraft Website": "Сайт ArtCraft", + "ArtCraft — more creative apps": "ArtCraft — другие творческие приложения", + "As Shot": "Как снято", + "As shot": "Как снято", + "Ascending": "По возрастанию", + "Aspect": "Пропорции", + "Aspect Ratio": "Соотношение сторон", + "Assisted Culling": "Автоматический отбор", + "Assisted Culling…": "Автоматический отбор…", + "Auto": "Авто", + "Auto Advance": "Автопереход", + "Auto Advance Off": "Автопереход выкл.", + "Auto Advance On": "Автопереход вкл.", + "Auto Advance after rating or flagging": "Автопереход после оценки или пометки флагом", + "Auto Advance: move to the next photo after rating or flagging": "Автопереход: переходить к следующему фото после оценки или пометки флагом", + "Auto Align": "Автовыравнивание", + "Auto B&W Mix": "Авто-микс Ч/Б", + "Auto Crop": "Автокадрирование", + "Auto Import": "Автоимпорт", + "Auto Import Now": "Выполнить автоимпорт сейчас", + "Auto Import Settings": "Настройки автоимпорта", + "Auto Mask": "Автомаска", + "Auto Settings": "Автонастройки", + "Auto Straighten": "Автовыравнивание горизонта", + "Auto Sync": "Автосинхронизация", + "Auto-Stack by Capture Time": "Автостопка по времени съёмки", + "Auto-Stack by Capture Time…": "Автостопка по времени съёмки…", + "Auto-Tag Photos from Tracklog": "Автопривязка фото по треку", + "Auto-Tag from Tracklog": "Автопривязка по треку", + "Auto-Tag from Tracklog…": "Автопривязка по треку…", + "Automatic": "Автоматически", + "Automatically Write Changes into XMP": "Автоматически записывать изменения в XMP", + "Automatically write changes into XMP sidecars": "Автоматически записывать изменения в XMP-файлы", + "Autumn Gold": "Осеннее золото", + "B&W": "Ч/Б", + "B&W Mix": "Микс Ч/Б", + "B&W Mixer": "Микшер Ч/Б", + "Back": "Назад", + "Back Up Library…": "Резервная копия библиотеки…", + "Back to Edit": "Вернуться к редактированию", + "Back to Edit (E)": "Вернуться к редактированию (E)", + "Back to Grid": "Вернуться к сетке", + "Background": "Фон", + "Balance": "Баланс", + "Basic": "Основные", + "Before": "До", + "Before/After (Y)": "До/после (Y)", + "Before/After Settings": "Настройки «до/после»", + "Before/After Split": "Разделение «до/после»", + "Before/After Split Top/Bottom": "Разделение «до/после» сверху/снизу", + "Before/After Top/Bottom": "До/после сверху/снизу", + "Begin Interaction": "Начать взаимодействие", + "Bit depth": "Разрядность", + "Black & White": "Чёрно-белое", + "Blacks": "Чёрные", + "Bleach Bypass": "Обход отбеливания", + "Bleached": "Выбеленный", + "Blending": "Наложение", + "Blue": "Синий", + "Blue Hour Boost": "Усиление синего часа", + "Blue Hue": "Тон синего", + "Blue Luminance": "Яркость синего", + "Blue Primary": "Основной синий", + "Blue Saturation": "Насыщенность синего", + "Blue channel": "Синий канал", + "Blue/Yellow Fringe": "Сине-жёлтая кайма", + "Blurry photos below the score are flagged as rejects.": "Размытые фото с оценкой ниже порога помечаются как отклонённые.", + "Boundary Warp": "Деформация границ", + "Bright & Airy": "Светлый и воздушный", + "Bring All to Front": "Показать все окна", + "Browse Folder": "Выбрать папку", + "Browse Folder…": "Выбрать папку…", + "Browse Profiles": "Обзор профилей", + "Browse…": "Обзор…", + "Brush": "Кисть", + "Brush Settings": "Настройки кисти", + "Brush Stroke": "Мазок кисти", + "Build Previews": "Создать просмотры", + "Build Smart Previews": "Создать смарт-просмотры", + "By Date": "По дате", + "By day (YYYY/YYYY-MM-DD)": "По дням (ГГГГ/ГГГГ-ММ-ДД)", + "By month (YYYY/YYYY-MM)": "По месяцам (ГГГГ/ГГГГ-ММ)", + "Calibration": "Калибровка", + "Camera": "Камера", + "Cameras and Cards": "Камеры и карты", + "Cancel": "Отмена", + "Cancel Download": "Отменить загрузку", + "Cancel Interaction": "Отменить взаимодействие", + "Cancel Preview Build": "Отменить создание просмотров", + "Candidate": "Кандидат", + "Caption": "Подпись", + "Capture Date": "Дата съёмки", + "Captured": "Снято", + "Catalog Statistics": "Статистика каталога", + "Check All": "Отметить все", + "Check Output Path": "Проверить путь вывода", + "Choose Another Library…": "Выбрать другую библиотеку…", + "Choose Edit Settings to Copy": "Выбрать настройки для копирования", + "Choose Edit Settings to Copy…": "Выбрать настройки для копирования…", + "Choose Folder…": "Выбрать папку…", + "Choose a photo in the grid or filmstrip": "Выберите фото в сетке или на ленте", + "Choose…": "Выбрать…", + "Cinematic": "Кинематографичный", + "City": "Город", + "Clarity": "Чёткость", + "Clean Studio": "Чистая студия", + "Clear": "Очистить", + "Clear Cache": "Очистить кэш", + "Clear Filters": "Сбросить фильтры", + "Clear Guides": "Убрать направляющие", + "Clear History": "Очистить историю", + "Clear Preview Cache": "Очистить кэш просмотров", + "Clear Quick Collection": "Очистить быструю коллекцию", + "Clear Reference": "Убрать эталон", + "Clear all": "Очистить всё", + "Clear form": "Очистить форму", + "Cleared History": "История очищена", + "Click Zoom Ratio": "Масштаб по щелчку", + "Click a tag to insert it. Examples are for IMG_0042.CR3 taken 14 Jan 2026, 05:58:48.": "Щёлкните по тегу, чтобы вставить его. Примеры приведены для IMG_0042.CR3, снятого 14 января 2026 в 05:58:48.", + "Click photos in the grid to toggle the keyword; Esc stops": "Щёлкайте по фото в сетке, чтобы переключать ключевое слово; Esc — остановить", + "Click the photo to pick a colour · ⇧-click adds more": "Щёлкните по фото, чтобы выбрать цвет · ⇧+щелчок добавляет ещё", + "Click the photo to pick a colour; ⇧-click adds more (up to 5)": "Щёлкните по фото, чтобы выбрать цвет; ⇧+щелчок добавляет ещё (до 5)", + "Clone": "Клонировать", + "Close": "Закрыть", + "Close Panel": "Закрыть панель", + "Close Window": "Закрыть окно", + "Close all": "Закрыть все", + "Close file": "Закрыть файл", + "Cloudy": "Облачно", + "Collapse All Stacks": "Свернуть все стопки", + "Color": "Цвет", + "Color Grading": "Цветокоррекция", + "Color Label Names": "Названия цветовых меток", + "Color Label Sets": "Наборы цветовых меток", + "Color Mixer": "Цветовой микшер", + "Color Noise Reduction": "Подавление цветового шума", + "Color Priority": "Приоритет цвета", + "Color Range": "Цветовой диапазон", + "Color space": "Цветовое пространство", + "Color space: sRGB (AVIF)": "Цветовое пространство: sRGB (AVIF)", + "Combine files…": "Объединить файлы…", + "Command Journal": "Журнал команд", + "Comments panel": "Панель комментариев", + "Compare": "Сравнение", + "Compare (Shift+C)": "Сравнение (Shift+C)", + "Compare Before and After": "Сравнить «до» и «после»", + "Compare needs at least two photos": "Для сравнения нужно минимум два фото", + "Component options": "Параметры компонента", + "Compression": "Сжатие", + "Concrete Cool": "Холодный бетон", + "Confirm before deleting": "Подтверждать удаление", + "Confirm before moving photos to Recently Deleted": "Подтверждать перемещение фото в «Недавно удалённые»", + "Constrain Crop": "Ограничить кадрирование", + "Continue Without Saving": "Продолжить без сохранения", + "Contrast": "Контрастность", + "Convert to DNG": "Преобразовать в DNG", + "Cool Fade": "Холодное выцветание", + "Cool Morning": "Холодное утро", + "Copy": "Копировать", + "Copy After's Settings to Before": "Скопировать настройки «после» в «до»", + "Copy Before's Settings to After": "Скопировать настройки «до» в «после»", + "Copy Edit Settings": "Копировать настройки правки", + "Copy History Step Settings to Before": "Скопировать настройки шага истории в «до»", + "Copy Metadata": "Копировать метаданные", + "Copy as DNG": "Копировать как DNG", + "Copy into the library (else use the files where they are)": "Копировать в библиотеку (иначе файлы используются там, где лежат)", + "Copy pages": "Копировать страницы", + "Copy the files (into the library's Originals/, or a folder you choose)": "Копировать файлы (в папку «Originals/» библиотеки или в выбранную папку)", + "Copy to": "Копировать в", + "Copy to Clipboard": "Копировать в буфер обмена", + "Copy: the files are copied to the folder below and the library uses the copies; the originals are left as they are.": "Копирование: файлы копируются в указанную ниже папку, а библиотека использует копии; оригиналы остаются как есть.", + "Copyright": "Авторские права", + "Copyright Info URL": "URL сведений об авторских правах", + "Copyright Status": "Статус авторских прав", + "Country": "Страна", + "Create Album": "Создать альбом", + "Create Album…": "Создать альбом…", + "Create Folder": "Создать папку", + "Create Folder…": "Создать папку…", + "Create New Mask": "Создать новую маску", + "Create PDF from clipboard": "Создать PDF из буфера обмена", + "Create PDF from file…": "Создать PDF из файла…", + "Create PDF from images…": "Создать PDF из изображений…", + "Create Preset": "Создать пресет", + "Create Preset…": "Создать пресет…", + "Create Proof Copy": "Создать пробную копию", + "Create Smart Album": "Создать смарт-альбом", + "Create Smart Album from Filter…": "Создать смарт-альбом из фильтра…", + "Create Smart Album…": "Создать смарт-альбом…", + "Create Stack": "Создать стопку", + "Create Version": "Создать версию", + "Create Version from Step": "Создать версию из шага", + "Create Virtual Copy": "Создать виртуальную копию", + "Creator": "Автор", + "Crisp Landscape": "Чёткий пейзаж", + "Crisp Vista": "Чёткая панорама", + "Crop": "Кадрирование", + "Crop & Rotate": "Кадрирование и поворот", + "Crop & Rotate (C)": "Кадрирование и поворот (C)", + "Crop Aspect": "Пропорции кадра", + "Cross Process": "Кросс-процесс", + "Culling": "Отбор", + "Curve": "Кривая", + "Custom": "Свои", + "Custom…": "Свои…", + "Cut": "Вырезать", + "Cut pages": "Вырезать страницы", + "Cycle Crop Overlay": "Переключить сетку кадрирования", + "Cycle Crop Overlay Orientation": "Переключить ориентацию сетки кадрирования", + "Cycle Info Overlay": "Переключить информационную накладку", + "Cycle Mask Overlay Color": "Переключить цвет наложения маски", + "Cycle Mask Overlay Mode": "Переключить режим наложения маски", + "Cylindrical": "Цилиндрическая", + "Dark & Moody": "Тёмный и атмосферный", + "Darken": "Затемнить", + "Darks": "Тёмные тона", + "Date Groups": "Группы по датам", + "Day": "День", + "Daylight": "Дневной свет", + "Decrease Brush Feather": "Уменьшить растушёвку кисти", + "Decrease Brush Size": "Уменьшить размер кисти", + "Deep Sky": "Глубокое небо", + "Defringe": "Убрать кайму", + "Deghost Amount": "Степень удаления артефактов", + "Dehaze": "Убрать дымку", + "Delete": "Удалить", + "Delete (⌫)": "Удалить (⌫)", + "Delete Album": "Удалить альбом", + "Delete All Masks": "Удалить все маски", + "Delete Color Label Set": "Удалить набор цветовых меток", + "Delete Export Preset": "Удалить пресет экспорта", + "Delete Filter Preset": "Удалить пресет фильтра", + "Delete Imported Profile": "Удалить импортированный профиль", + "Delete Keyword": "Удалить ключевое слово", + "Delete Keyword Set": "Удалить набор ключевых слов", + "Delete Mask": "Удалить маску", + "Delete Metadata Preset": "Удалить пресет метаданных", + "Delete Permanently": "Удалить навсегда", + "Delete Photo": "Удалить фото", + "Delete Photos": "Удалить фото", + "Delete Point Color Sample": "Удалить точечный образец цвета", + "Delete Point Curve Preset": "Удалить пресет точечной кривой", + "Delete Preset": "Удалить пресет", + "Delete Red Eye Correction": "Удалить коррекцию красных глаз", + "Delete Spot": "Удалить пятно", + "Delete Version": "Удалить версию", + "Delete all": "Удалить все", + "Delete all spots": "Удалить все пятна", + "Delete mask": "Удалить маску", + "Delete them": "Удалить их", + "Density": "Плотность", + "Depth Range": "Диапазон глубины", + "Descending": "По убыванию", + "Describe…": "Описать…", + "Deselect": "Снять выделение", + "Deselect All": "Снять выделение со всех", + "Desert Heat": "Жар пустыни", + "Desert Warmth": "Тепло пустыни", + "Detail": "Детализация", + "Detail (D)": "Детализация (D)", + "Details": "Подробности", + "Diagonal": "По диагонали", + "Discard Preview Cache": "Удалить кэш просмотров", + "Display P3": "Display P3", + "Display theme": "Тема оформления", + "Distortion": "Искажение", + "Document properties…": "Свойства документа…", + "Don't enlarge": "Не увеличивать", + "Done": "Готово", + "Drag along the horizon; double-click for Auto": "Ведите вдоль горизонта; двойной щелчок — авто", + "Drag over an eye on the photo; the pupil inside is found automatically.": "Наведите на глаз на фото; зрачок внутри будет найден автоматически.", + "Draw Guides": "Нарисовать направляющие", + "Duplicate": "Дублировать", + "Duplicate Mask": "Дублировать маску", + "Duplicate and Invert Mask": "Дублировать и инвертировать маску", + "Each / starts a folder level; tags are filled in per photo (a level with missing metadata is \"unknown\").": "Каждый символ / начинает новый уровень папок; теги подставляются для каждого фото (уровень без метаданных называется «unknown»).", + "Edit": "Правка", + "Edit (E)": "Правка (E)", + "Edit Capture Time": "Изменить время съёмки", + "Edit Capture Time…": "Изменить время съёмки…", + "Edit Color Label Names": "Изменить названия цветовых меток", + "Edit Color Label Names…": "Изменить названия цветовых меток…", + "Edit Copy for External Editor": "Править копию во внешнем редакторе", + "Edit Info": "Править сведения", + "Edit Label Names…": "Изменить названия меток…", + "Edit Mask Component": "Изменить компонент маски", + "Edit Sections": "Изменить разделы", + "Edit Smart Album": "Изменить смарт-альбом", + "Edit Smart Album…": "Изменить смарт-альбом…", + "Edit Spot": "Изменить пятно", + "Edit in External Editor": "Править во внешнем редакторе", + "Edited": "Изменено", + "Effect": "Эффект", + "Effects": "Эффекты", + "Enable Lens Corrections": "Включить коррекцию объектива", + "End Interaction": "Завершить взаимодействие", + "English": "Английский", + "Enter Full Screen": "Полноэкранный режим", + "Erase": "Стереть", + "Every EXIF, GPS and XMP field in the file": "Все поля EXIF, GPS и XMP в файле", + "Existing previews": "Имеющиеся просмотры", + "Exit": "Выход", + "Expand All Stacks": "Развернуть все стопки", + "Expand/Collapse Stack": "Развернуть/свернуть стопку", + "Export": "Экспорт", + "Export Album with Preset": "Экспортировать альбом с пресетом", + "Export Album…": "Экспортировать альбом…", + "Export As…": "Экспортировать как…", + "Export Group…": "Экспортировать группу…", + "Export Now": "Экспортировать сейчас", + "Export Point Curve Presets": "Экспортировать пресеты точечной кривой", + "Export Point Curve Presets…": "Экспортировать пресеты точечной кривой…", + "Export Presets": "Экспортировать пресеты", + "Export Presets…": "Экспортировать пресеты…", + "Export User Presets…": "Экспортировать пользовательские пресеты…", + "Export with Preset": "Экспорт с пресетом", + "Export with Previous": "Экспорт с предыдущими настройками", + "Exporting": "Экспорт", + "Export…": "Экспорт…", + "Exposure": "Экспозиция", + "Exposure (shutter · aperture · ISO)": "Экспозиция (выдержка · диафрагма · ISO)", + "Extended Description": "Расширенное описание", + "External Editor": "Внешний редактор", + "Faded Matte": "Выцветший матовый", + "Faded Slide": "Выцветший слайд", + "Faded Urban": "Выцветший урбан", + "Favorite Preset": "Избранный пресет", + "Favorite Profile": "Избранный профиль", + "Favorites": "Избранное", + "Feather": "Растушёвка", + "File": "Файл", + "File & date": "Файл и дата", + "File Name": "Имя файла", + "File Path": "Путь к файлу", + "File Size": "Размер файла", + "File name": "Имя файла", + "Fill Edges": "Заполнить края", + "Film": "Плёнка", + "Filmstrip": "Лента", + "Filmstrip (/)": "Лента (/)", + "Filter": "Фильтр", + "Filter Bar": "Панель фильтров", + "Filter Presets": "Пресеты фильтров", + "Filter bar": "Панель фильтров", + "Filter fields": "Поля фильтра", + "Find Dust Spots": "Найти пылинки", + "Find Missing Photos": "Найти отсутствующие фото", + "Find Missing Photos…": "Найти отсутствующие фото…", + "Find Similar Photos": "Найти похожие фото", + "Find tools and commands…": "Найти инструменты и команды…", + "Find…": "Найти…", + "Fit": "Вписать", + "Fit (F)": "Вписать (F)", + "Fit on Screen": "Вписать в экран", + "Fit to width": "По ширине", + "Fit visible": "Вписать видимое", + "Flag": "Флаг", + "Flag as Pick": "Пометить как выбранное", + "Flag as Reject": "Пометить как отклонённое", + "Flash": "Вспышка", + "Flip H": "Отразить по гор.", + "Flip Horizontal": "Отразить по горизонтали", + "Flip Overlay (⇧O)": "Отразить наложение (⇧O)", + "Flip V": "Отразить по верт.", + "Flip Vertical": "Отразить по вертикали", + "Flow": "Поток", + "Fluorescent": "Флуоресцентная лампа", + "Focal length": "Фокусное расстояние", + "Folder": "Папка", + "Folder name (renamed on disk; its photos follow)": "Имя папки (переименовывается на диске; фото следуют за ней)", + "Folders": "Папки", + "Folders and {date} use the capture time; a photo without one uses today's date.": "Папки и {date} используют время съёмки; фото без него получает сегодняшнюю дату.", + "Food": "Еда", + "Forget Unchanged Local Photos": "Забыть неизменённые локальные фото", + "Forget unchanged photos": "Забыть неизменённые фото", + "Form fields panel": "Панель полей формы", + "Format": "Формат", + "Forward": "Вперёд", + "Free": "Свободно", + "Fresh & Bright": "Свежий и яркий", + "Friday": "Пятница", + "Full": "Полный", + "Full Screen Preview": "Полноэкранный просмотр", + "Full screen mode": "Полноэкранный режим", + "Full size": "Полный размер", + "GPS Track Log": "GPS-трек", + "GPU Rendering": "Рендеринг на GPU", + "General": "Общие", + "Geometry": "Геометрия", + "Get Develop Settings": "Получить настройки коррекции", + "Global Hue": "Общий тон", + "Global Luminance": "Общая яркость", + "Global Saturation": "Общая насыщенность", + "Glossy": "Глянцевый", + "Go to History Step": "Перейти к шагу истории", + "Golden": "Золотой", + "Golden Glow": "Золотое сияние", + "Golden Hour": "Золотой час", + "Grain": "Зерно", + "Graphic": "Графичный", + "Green": "Зелёный", + "Green Amount": "Количество зелёного", + "Green Hue": "Тон зелёного", + "Green Hue High": "Тон зелёного (верх)", + "Green Hue Low": "Тон зелёного (низ)", + "Green Luminance": "Яркость зелёного", + "Green Primary": "Основной зелёный", + "Green Saturation": "Насыщенность зелёного", + "Green channel": "Зелёный канал", + "Grid": "Сетка", + "Grid Info": "Информация сетки", + "Gritty Street": "Жёсткая улица", + "Group": "Группа", + "Group by Date: Automatic": "Группировка по дате: автоматически", + "Group by Day": "Группировать по дням", + "Group by Month": "Группировать по месяцам", + "Group by Year": "Группировать по годам", + "Group by date": "Группировать по дате", + "Group into Stack": "Сгруппировать в стопку", + "Guided": "Направляемый", + "Guided Upright": "Направляемое выравнивание", + "Guided Upright Guides": "Направляющие выравнивания", + "HDR Merge": "Слияние HDR", + "HDR Panorama Merge": "Слияние HDR-панорамы", + "HDR Panorama with Last Settings": "HDR-панорама с последними настройками", + "HDR Panorama…": "HDR-панорама…", + "HDR with Last Settings": "HDR с последними настройками", + "HDR…": "HDR…", + "Heal": "Лечение", + "Heal the small, soft dark spots sensor dust leaves on smooth areas": "Убирать мелкие мягкие тёмные пятна от пыли на матрице на ровных участках", + "Height": "Высота", + "Height (px)": "Высота (px)", + "Help": "Справка", + "Hide LightCraft": "Скрыть LightCraft", + "Hide Mask": "Скрыть маску", + "Hide Others": "Скрыть остальные", + "Hides this shortcut only; the folder and its photos stay as they are": "Скрывает только этот ярлык; папка и её фото остаются как есть", + "High": "Высокий", + "High Contrast B&W": "Контрастное Ч/Б", + "Highlight": "Свет", + "Highlights": "Света", + "Highlights Hue": "Тон светов", + "Highlights Luminance": "Яркость светов", + "Highlights Saturation": "Насыщенность светов", + "Highlights split": "Разделение светов", + "Histogram": "Гистограмма", + "History": "История", + "History & Activity (Y)": "История и активность (Y)", + "Horizontal": "По горизонтали", + "Horizontal text": "Горизонтальный текст", + "Hue": "Тон", + "Hue Range": "Диапазон тонов", + "If file exists": "Если файл существует", + "Image": "Изображение", + "Import": "Импорт", + "Import Date": "Дата импорта", + "Import Photos": "Импорт фото", + "Import Photos…": "Импорт фото…", + "Import Point Curve Presets": "Импортировать пресеты точечной кривой", + "Import Point Curve Presets…": "Импортировать пресеты точечной кривой…", + "Import Presets": "Импортировать пресеты", + "Import Presets…": "Импортировать пресеты…", + "Import Profiles": "Импортировать профили", + "Import Profiles & Presets…": "Импортировать профили и пресеты…", + "Import from Device": "Импорт с устройства", + "Import from Folder…": "Импорт из папки…", + "Import photos with File → Import Photos… (Cmd+Shift+I), or drop them here": "Импортируйте фото через Файл → Импорт фото… (Cmd+Shift+I) или перетащите их сюда", + "Imported Photos": "Импортированные фото", + "Importing": "Импорт", + "Importing scans the source for photos; it doesn't add the folder to Local (use Local → Browse Folder… to work in a folder without importing).": "При импорте источник сканируется на фото; папка не добавляется в «Локальное» (чтобы работать с папкой без импорта, используйте Локальное → Выбрать папку…).", + "In Recently Deleted": "В «Недавно удалённых»", + "In library": "В библиотеке", + "In memory — nothing is saved": "В памяти — ничего не сохраняется", + "In use": "Используется", + "In-memory session: these settings last until LightCraft quits.": "Сеанс в памяти: эти настройки действуют до закрытия LightCraft.", + "Include subfolders": "Включая вложенные папки", + "Increase Brush Feather": "Увеличить растушёвку кисти", + "Increase Brush Size": "Увеличить размер кисти", + "Info": "Информация", + "Info (I)": "Информация (I)", + "Info overlay": "Информационная накладка", + "Inspect Photo": "Просмотр фото", + "Instant ’70s": "Мгновенный стиль 70-х", + "Interface": "Интерфейс", + "Interface language": "Язык интерфейса", + "Intersect": "Пересечение", + "Intersect with": "Пересечь с", + "Into one folder": "В одну папку", + "Invert": "Инвертировать", + "Invert Mask": "Инвертировать маску", + "JPEG (Large)": "JPEG (большой)", + "JPEG (Small)": "JPEG (малый)", + "Join the ArtCraft Discord": "Присоединяйтесь к Discord ArtCraft", + "Join the ArtCraft Discord…": "Присоединиться к Discord ArtCraft…", + "Keep in Local": "Оставить в «Локальном»", + "Keep it on a drive with room: smart previews are about 1 MB per photo. The thumbnail cache stays in the library.": "Храните их на диске со свободным местом: смарт-просмотр занимает около 1 МБ на фото. Кэш миниатюр остаётся в библиотеке.", + "Keyboard Shortcuts": "Сочетания клавиш", + "Keyboard shortcuts": "Сочетания клавиш", + "Keyword": "Ключевое слово", + "Keyword Painter": "Кисть ключевых слов", + "Keyword Set": "Набор ключевых слов", + "Keyword Sets": "Наборы ключевых слов", + "Keyword Suggestions": "Подсказки ключевых слов", + "Keyword to paint": "Ключевое слово для нанесения", + "Keywords": "Ключевые слова", + "Keywords (K)": "Ключевые слова (K)", + "Keywords you add appear here; ⌥1–⌥9 apply them.": "Добавленные вами ключевые слова появятся здесь; ⌥1–⌥9 применяют их.", + "Landscape": "Пейзаж", + "Language": "Язык", + "Largest long edge the Detail view renders at; larger is sharper on big displays but slower.": "Максимальная длинная сторона, с которой рисуется режим «Детализация»; больше — чётче на больших экранах, но медленнее.", + "Last": "Последний", + "Last view": "Последний вид", + "Lateral Chromatic Aberration": "Боковая хроматическая аберрация", + "Layer": "Слой", + "Layers": "Слои", + "Leave them": "Оставить их", + "Lens": "Объектив", + "Lens Corrections": "Коррекция объектива", + "Level": "Уровень", + "Library": "Библиотека", + "Library Info": "Сведения о библиотеке", + "Library Originals": "Оригиналы библиотеки", + "Library Preferences": "Настройки библиотеки", + "Library State": "Состояние библиотеки", + "Licence:": "Лицензия:", + "Light": "Светлый", + "LightCraft Default": "По умолчанию в LightCraft", + "LightCraft Help": "Справка LightCraft", + "LightCraft Preset": "Пресет LightCraft", + "LightCraft Presets": "Пресеты LightCraft", + "LightCraft Website": "Сайт LightCraft", + "LightCraft couldn't find where to keep your library.": "LightCraft не удалось найти место для хранения вашей библиотеки.", + "LightCraft couldn't open the library at": "LightCraft не удалось открыть библиотеку в", + "LightCraft on GitHub": "LightCraft на GitHub", + "LightCraft website": "Сайт LightCraft", + "LightCraft — Second Window": "LightCraft — второе окно", + "Lights": "Светлые тона", + "Limit file size (KB, 0 = off)": "Ограничить размер файла (КБ, 0 = выкл.)", + "Linear Gradient": "Линейный градиент", + "List Albums": "Список альбомов", + "List Develop Controls": "Список элементов управления коррекцией", + "List History": "Список истории", + "List Presets": "Список пресетов", + "List Profiles": "Список профилей", + "List this folder in Local from now on": "Показывать эту папку в «Локальном» и дальше", + "Local": "Локальное", + "Local folders": "Локальные папки", + "Local library — no cloud account needed": "Локальная библиотека — учётная запись в облаке не нужна", + "Locate Missing File…": "Указать отсутствующий файл…", + "Locate Photo": "Найти фото", + "Location": "Местоположение", + "Long Edge": "Длинная сторона", + "Looking for photos…": "Поиск фото…", + "Lossless": "Без потерь", + "Low": "Низкий", + "Luminance": "Яркость", + "Luminance Range": "Диапазон яркости", + "Lush Greens": "Сочная зелень", + "Magenta": "Пурпурный", + "Magenta Hue": "Тон пурпурного", + "Magenta Luminance": "Яркость пурпурного", + "Magenta Saturation": "Насыщенность пурпурного", + "Make Candidate the Select": "Сделать кандидата выбранным", + "Manual": "Вручную", + "Marquee zoom": "Масштаб рамкой", + "Masking": "Маски", + "Masking (M)": "Маски (M)", + "Masks": "Маски", + "Match": "Совпадение", + "Match Total Exposures": "Совпадение общей экспозиции", + "Matte": "Матовый", + "Matte Soft": "Мягкий матовый", + "Med": "Средний", + "Medium Contrast": "Средний контраст", + "Megapixels": "Мегапиксели", + "Memory Budget": "Лимит памяти", + "Memory Usage": "Использование памяти", + "Memory for caches": "Память для кэшей", + "Menu": "Меню", + "Merge": "Слияние", + "Merge Keywords": "Объединить ключевые слова", + "Merge into…": "Объединить с…", + "Merging…": "Слияние…", + "Meta's terms, not LightCraft's. Downloading it means accepting them.": "Условия Meta, а не LightCraft. Загрузка означает их принятие.", + "Metadata": "Метаданные", + "Metadata Preset": "Пресет метаданных", + "Metadata Presets": "Пресеты метаданных", + "Metadata preset": "Пресет метаданных", + "Midpoint": "Средняя точка", + "Midtones": "Средние тона", + "Midtones Hue": "Тон средних тонов", + "Midtones Luminance": "Яркость средних тонов", + "Midtones Saturation": "Насыщенность средних тонов", + "Midtones split": "Разделение средних тонов", + "Minimize": "Свернуть", + "Missing Photos": "Отсутствующие фото", + "Misty Morning": "Туманное утро", + "Mode": "Режим", + "Modified Date": "Дата изменения", + "Monday": "Понедельник", + "Mono Red Filter": "Моно с красным фильтром", + "Mono Rich": "Насыщенное моно", + "Mono Soft": "Мягкое моно", + "Monochrome": "Монохром", + "Month": "Месяц", + "Moody": "Атмосферный", + "Moody Portrait": "Атмосферный портрет", + "More": "Ещё", + "More preset options": "Другие параметры пресета", + "Move": "Переместить", + "Move Album": "Переместить альбом", + "Move Down in Stack": "Опустить в стопке", + "Move Folder": "Переместить папку", + "Move Folder To…": "Переместить папку в…", + "Move Mask": "Переместить маску", + "Move Preset to Group": "Переместить пресет в группу", + "Move Up in Stack": "Поднять в стопке", + "Move the files (into the library's Originals/, or a folder you choose), removing them from the source": "Переместить файлы (в папку «Originals/» библиотеки или в выбранную папку), удалив их из источника", + "Move them": "Переместить их", + "Move to": "Переместить в", + "Move to Group": "Переместить в группу", + "Move: the files are moved to the folder below; each original (and its XMP sidecar) is removed from the source only after its copy is verified. Duplicates and files that fail stay where they are.": "Перемещение: файлы перемещаются в указанную ниже папку; каждый оригинал (и его XMP-файл) удаляется из источника только после проверки копии. Дубликаты и файлы с ошибками остаются на месте.", + "Muted": "Приглушённый", + "Muted Film": "Приглушённая плёнка", + "My Photos": "Мои фото", + "My Photos Panel": "Панель «Мои фото»", + "Name": "Название", + "Named": "Именованные", + "Names appear in the label menu, the filter bar and the Info panel, and are written to XMP. Empty = the colour's name.": "Названия показываются в меню меток, на панели фильтров и в панели «Информация» и записываются в XMP. Пусто = название цвета.", + "Navigator": "Навигатор", + "Needs Work": "Требует доработки", + "Neon Dusk": "Неоновые сумерки", + "Neon Night": "Неоновая ночь", + "Neutral": "Нейтральный", + "Never": "Никогда", + "New": "Новый", + "New Album": "Новый альбом", + "New Album…": "Новый альбом…", + "New Folder…": "Новая папка…", + "New Group…": "Новая группа…", + "New Smart Album": "Новый смарт-альбом", + "New Smart Album from Filter…": "Новый смарт-альбом из фильтра…", + "New Smart Album…": "Новый смарт-альбом…", + "New album": "Новый альбом", + "New album…": "Новый альбом…", + "New blank PDF": "Новый пустой PDF", + "New name": "Новое имя", + "New…": "Новый…", + "Next Photo": "Следующее фото", + "Night Blue": "Ночной синий", + "No": "Нет", + "No Camera or Card Found": "Камера или карта не найдены", + "No Date Groups": "Без групп по датам", + "No Stars": "Без звёзд", + "No Tool": "Нет инструмента", + "No automatic versions.": "Автоматических версий нет.", + "No camera": "Нет камеры", + "No date": "Нет даты", + "No dust spots found": "Пылинки не найдены", + "No edits yet.": "Правок пока нет.", + "No exposure information": "Нет данных об экспозиции", + "No filter presets yet": "Пресетов фильтров пока нет", + "No lens data embedded in this file (DNG lens opcodes only).": "В этом файле нет данных об объективе (только опкоды объектива DNG).", + "No masks yet. Choose a mask type above.": "Масок пока нет. Выберите тип маски выше.", + "No matching photos": "Нет подходящих фото", + "No metadata found": "Метаданные не найдены", + "No new presets": "Новых пресетов нет", + "No photo selected": "Фото не выбрано", + "No photos": "Нет фото", + "No photos found": "Фото не найдены", + "No photos in this folder": "В этой папке нет фото", + "No photos match the active filters": "Нет фото, соответствующих активным фильтрам", + "No photos match the filters (View → Clear Filters)": "Нет фото, соответствующих фильтрам (Вид → Сбросить фильтры)", + "No raw photos yet: cameras appear here once their photos are in the library.": "RAW-фото пока нет: камеры появятся здесь, когда их фото попадут в библиотеку.", + "No reference photo": "Нет эталонного фото", + "No usable GPU found: rendering on the CPU": "Подходящий GPU не найден: рендеринг на CPU", + "No versions yet. Create one to keep this look.": "Версий пока нет. Создайте версию, чтобы сохранить этот вид.", + "Noise": "Шум", + "Noise Reduction": "Подавление шума", + "Non-raw photos": "Не RAW-фото", + "None": "Нет", + "Nothing to compare": "Нечего сравнивать", + "Nothing to survey": "Нечего просматривать в обзоре", + "Nudge Develop Value": "Подправить значение коррекции", + "OK": "OK", + "Object": "Объект", + "Off": "Выкл.", + "Offset X": "Смещение по X", + "Offset Y": "Смещение по Y", + "Opacity": "Непрозрачность", + "Opacity (%)": "Непрозрачность (%)", + "Open Library": "Открыть библиотеку", + "Open Library…": "Открыть библиотеку…", + "Open in": "Открыть в", + "Open in Detail": "Открыть в режиме «Детализация»", + "Open the place in OpenStreetMap": "Открыть место в OpenStreetMap", + "Open…": "Открыть…", + "Optics": "Оптика", + "Optimize Library": "Оптимизировать библиотеку", + "Optional name": "Название (необязательно)", + "Options": "Параметры", + "Orange": "Оранжевый", + "Orange Hue": "Тон оранжевого", + "Orange Luminance": "Яркость оранжевого", + "Orange Saturation": "Насыщенность оранжевого", + "Orientation": "Ориентация", + "Original": "Оригинал", + "Original + Settings": "Оригинал + настройки", + "Original files": "Оригиналы файлов", + "Original offline · editing the smart preview": "Оригинал недоступен · правка смарт-просмотра", + "Original offline · no smart preview": "Оригинал недоступен · нет смарт-просмотра", + "Originals": "Оригиналы", + "Other images (JPEG, PNG, TIFF, HEIC…)": "Другие изображения (JPEG, PNG, TIFF, HEIC…)", + "Overlay": "Наложение", + "Overwrite": "Перезаписать", + "Pages": "Страницы", + "Paint": "Закрасить", + "Paint only areas like the one under the brush": "Закрашивать только области, похожие на ту, что под кистью", + "Paint over a distraction on the photo to remove it.": "Закрасьте лишнее на фото, чтобы убрать это.", + "Paint overlay": "Наложение закраски", + "Panorama Merge": "Слияние панорамы", + "Panorama with Last Settings": "Панорама с последними настройками", + "Panorama…": "Панорама…", + "Parametric curve": "Параметрическая кривая", + "Paste": "Вставить", + "Paste Edit Settings": "Вставить настройки правки", + "Paste Metadata": "Вставить метаданные", + "Paste Selected Settings": "Вставить выбранные настройки", + "Paste Selected Settings…": "Вставить выбранные настройки…", + "Paste Settings from Previous": "Вставить настройки из предыдущего", + "Paste pages": "Вставить страницы", + "Pastel Haze": "Пастельная дымка", + "Percent": "Проценты", + "Percentage": "Процент", + "Performance": "Производительность", + "Perspective": "Перспектива", + "Pet Eye": "Глаза питомца", + "Pet Eye Catchlight": "Блик в глазах питомца", + "Photo": "Фото", + "Photo Grid": "Сетка фото", + "Photo Grid (G)": "Сетка фото (G)", + "Photo Merge": "Слияние фото", + "Photo ▸ Edit in External Editor (⇧⌘E) renders a 16-bit TIFF copy, stacks it with the original and opens it here (an app name on macOS, a program path elsewhere).": "Фото ▸ Править во внешнем редакторе (⇧⌘E) создаёт 16-битную TIFF-копию, помещает её в стопку с оригиналом и открывает здесь (имя приложения в macOS, путь к программе в других системах).", + "Photos": "Фото", + "Photos seen in Local but never added or changed leave the catalog when their folder hasn't been browsed for this long (checked when the library opens). Files stay on disk; browsing the folder shows them again.": "Фото, которые видели в «Локальном», но ни разу не добавляли и не меняли, исключаются из каталога, если папку не открывали так долго (проверяется при открытии библиотеки). Файлы остаются на диске; при просмотре папки они появятся снова.", + "Photos that arrive in this folder (tethering, a scanner, a sync app) are added as soon as they're complete.": "Фото, поступающие в эту папку (тетеринг, сканер, приложение синхронизации), добавляются, как только будут готовы.", + "Pick": "Выбрать", + "Pick a colour on the photo with the eyedropper.": "Выберите цвет на фото пипеткой.", + "Pick the sharpest photo of each burst": "Выбирать самое чёткое фото в каждой серии", + "Picked": "Выбрано", + "Picks": "Выбранные", + "Pixels": "Пиксели", + "Point Color": "Точечный цвет", + "Point Curve": "Точечная кривая", + "Point Curve Presets": "Пресеты точечной кривой", + "Point curve": "Точечная кривая", + "Portrait": "Портрет", + "Position": "Положение", + "Preferences": "Настройки", + "Preferences…": "Настройки…", + "Preset": "Пресет", + "Preset name": "Название пресета", + "Presets": "Пресеты", + "Presets & Profiles": "Пресеты и профили", + "Presets (Shift+P)": "Пресеты (Shift+P)", + "Presets ▾": "Пресеты ▾", + "Preview": "Просмотр", + "Preview Build Progress": "Ход создания просмотров", + "Preview only": "Только просмотр", + "Preview size": "Размер просмотра", + "Previews": "Просмотры", + "Previous Photo": "Предыдущее фото", + "Print": "Печать", + "Print…": "Печать…", + "Profile": "Профиль", + "Profile Browser": "Обозреватель профилей", + "Profile Distortion": "Дисторсия профиля", + "Profile Menu": "Меню профилей", + "Profile Vignetting": "Виньетирование профиля", + "Profile:": "Профиль:", + "Profiles": "Профили", + "Projection": "Проекция", + "Properties": "Свойства", + "Public domain": "Общественное достояние", + "Pupil Size": "Размер зрачка", + "Purple": "Фиолетовый", + "Purple Amount": "Количество фиолетового", + "Purple Hue": "Тон фиолетового", + "Purple Hue High": "Тон фиолетового (верх)", + "Purple Hue Low": "Тон фиолетового (низ)", + "Purple Luminance": "Яркость фиолетового", + "Purple Saturation": "Насыщенность фиолетового", + "Put the locations you removed from Local back (no files change)": "Вернуть в «Локальное» удалённые вами места (файлы не меняются)", + "Quality": "Качество", + "Query Photos": "Запросить фото", + "Quick Collection": "Быстрая коллекция", + "Quick Develop": "Быстрая коррекция", + "Quiet Green": "Тихий зелёный", + "Quit": "Выход", + "Quit LightCraft": "Выйти из LightCraft", + "Radial Gradient": "Радиальный градиент", + "Radius": "Радиус", + "Random": "Случайно", + "Range": "Диапазон", + "Rating": "Оценка", + "Ratings & flags": "Оценки и флаги", + "Raw default": "Стандарт RAW", + "Raw defaults": "Стандарты RAW", + "Raw files": "RAW-файлы", + "Raw photos": "RAW-фото", + "Raw photos as DNG, with the edits embedded. Size, color and output options don't apply.": "RAW-фото как DNG со встроенными правками. Параметры размера, цвета и вывода не применяются.", + "Read Metadata from File": "Прочитать метаданные из файла", + "Read mode": "Режим чтения", + "Read the SAM License": "Прочитать лицензию SAM", + "Reading": "Чтение", + "Reading folder…": "Чтение папки…", + "Recently Added": "Недавно добавленные", + "Recently Deleted": "Недавно удалённые", + "Red": "Красный", + "Red Eye": "Красные глаза", + "Red Hue": "Тон красного", + "Red Luminance": "Яркость красного", + "Red Primary": "Основной красный", + "Red Saturation": "Насыщенность красного", + "Red channel": "Красный канал", + "Red/Cyan Fringe": "Красно-голубая кайма", + "Redo": "Повторить", + "Reference": "Эталон", + "Reference View": "Режим эталона", + "Reference the files where they are": "Ссылаться на файлы там, где они лежат", + "Refine Mask Edges": "Уточнить края маски", + "Refresh Source": "Обновить источник", + "Refresh Source (/)": "Обновить источник (/)", + "Reject": "Отклонить", + "Reject below focus": "Отклонять при недостаточной резкости", + "Rejected": "Отклонено", + "Rejects": "Отклонённые", + "Reload from Disk": "Перезагрузить с диска", + "Remove": "Удалить", + "Remove (H)": "Удалить (H)", + "Remove CA": "Убрать ХА", + "Remove Chromatic Aberration": "Убрать хроматические аберрации", + "Remove from Album": "Удалить из альбома", + "Remove from Favorites": "Удалить из избранного", + "Remove from Local": "Убрать из «Локального»", + "Remove from Photo": "Убрать с фото", + "Remove from Selected Photos": "Убрать с выбранных фото", + "Remove from Stack": "Убрать из стопки", + "Remove from survey": "Убрать из обзора", + "Remove location info": "Удалить сведения о местоположении", + "Remove this filter": "Убрать этот фильтр", + "Remove this group": "Убрать эту группу", + "Remove this rule": "Убрать это условие", + "Rename": "Переименовать", + "Rename Album": "Переименовать альбом", + "Rename Folder": "Переименовать папку", + "Rename Folder…": "Переименовать папку…", + "Rename Keyword": "Переименовать ключевое слово", + "Rename Keyword…": "Переименовать ключевое слово…", + "Rename Mask": "Переименовать маску", + "Rename Photos": "Переименовать фото", + "Rename Photos…": "Переименовать фото…", + "Rename Preset": "Переименовать пресет", + "Rename Preview": "Предпросмотр переименования", + "Rename Template Tags": "Теги шаблона переименования", + "Rename Version": "Переименовать версию", + "Rename…": "Переименовать…", + "Rendering": "Рендеринг", + "Rendering…": "Рендеринг…", + "Reset": "Сбросить", + "Reset All Curves": "Сбросить все кривые", + "Reset Before to Import State": "Сбросить «до» к состоянию при импорте", + "Reset Control": "Сбросить элемент управления", + "Reset Crop": "Сбросить кадрирование", + "Reset Edits": "Сбросить правки", + "Reset Parametric Curve": "Сбросить параметрическую кривую", + "Reset Section": "Сбросить раздел", + "Reset Tone Curve": "Сбросить тоновую кривую", + "Reset all curves: point curves (all channels) and parametric (double-click a channel to reset only that one)": "Сбросить все кривые: точечные (все каналы) и параметрическую (двойной щелчок по каналу сбрасывает только его)", + "Reset all edits (Cmd+Shift+R)": "Сбросить все правки (Cmd+Shift+R)", + "Reshuffle": "Перемешать", + "Resize to": "Изменить размер до", + "Resolution (ppi)": "Разрешение (ppi)", + "Restore": "Восстановить", + "Restore Library from Backup…": "Восстановить библиотеку из резервной копии…", + "Restore Version": "Восстановить версию", + "Retouch": "Ретушь", + "Revert": "Вернуть", + "Review Import": "Проверить импорт", + "Right-click a photo ▸ Set as Reference Photo, then View → Reference View (Shift+R)": "Щёлкните фото правой кнопкой ▸ Назначить эталонным фото, затем Вид → Режим эталона (Shift+R)", + "Right-click a preset to delete it": "Щёлкните пресет правой кнопкой, чтобы удалить его", + "Rights Usage Terms": "Условия использования прав", + "Rotate": "Повернуть", + "Rotate Crop Aspect": "Повернуть пропорции кадра", + "Rotate Left": "Повернуть влево", + "Rotate Right": "Повернуть вправо", + "Roughness": "Шероховатость", + "Roundness": "Округлость", + "SAM 3 download stopped: it resumes where it left off next time.": "Загрузка SAM 3 остановлена: в следующий раз она продолжится с того же места.", + "Same as raw default": "Как стандарт RAW", + "Sample Mask Color": "Взять образец цвета маски", + "Sample a colour on the photo": "Взять образец цвета на фото", + "Saturation": "Насыщенность", + "Saturation Range": "Диапазон насыщенности", + "Saturday": "Суббота", + "Save": "Сохранить", + "Save As…": "Сохранить как…", + "Save Color Label Set": "Сохранить набор цветовых меток", + "Save Current Filter as Preset…": "Сохранить текущий фильтр как пресет…", + "Save Current Keywords as Set…": "Сохранить текущие ключевые слова как набор…", + "Save Export Preset": "Сохранить пресет экспорта", + "Save Filter Preset": "Сохранить пресет фильтра", + "Save Keyword Set": "Сохранить набор ключевых слов", + "Save Metadata Preset": "Сохранить пресет метаданных", + "Save Metadata Preset…": "Сохранить пресет метаданных…", + "Save Metadata to File": "Сохранить метаданные в файл", + "Save Point Curve Preset": "Сохранить пресет точечной кривой", + "Save Point Curve…": "Сохранить точечную кривую…", + "Save as Preset": "Сохранить как пресет", + "Save as Smart Album…": "Сохранить как смарт-альбом…", + "Save as set": "Сохранить как набор", + "Save as…": "Сохранить как…", + "Save preset": "Сохранить пресет", + "Scale": "Масштаб", + "Scanning": "Сканирование", + "Scores stay on the photos: filter or make smart albums with Focus and Best of Similar Shots.": "Оценки остаются на фото: фильтруйте или создавайте смарт-альбомы по Focus и Best of Similar Shots.", + "Screen": "Экран", + "Search Photos": "Поиск фото", + "Search…": "Поиск…", + "Seasons": "Времена года", + "Second Window": "Второе окно", + "Select": "Выбрать", + "Select All": "Выбрать все", + "Select Luminance Range": "Выбрать диапазон яркости", + "Select Mask": "Выбрать маску", + "Select Photos": "Выбрать фото", + "Select Spot": "Выбрать пятно", + "Select a photo to edit": "Выберите фото для правки", + "Select by": "Выбрать по", + "Select by Flag, Rating or Label": "Выбрать по флагу, оценке или метке", + "Select photos, then choose View → Survey (N)": "Выберите фото, затем Вид → Обзор (N)", + "Select two photos, then choose View → Compare (Shift+C)": "Выберите два фото, затем Вид → Сравнение (Shift+C)", + "Selected": "Выделено", + "Selenium Tone": "Селеновое тонирование", + "Send Feedback…": "Отправить отзыв…", + "Sepia Tone": "Сепия", + "Services": "Службы", + "Set Before": "Назначить «до»", + "Set Color Label": "Назначить цветовую метку", + "Set Crop": "Задать кадрирование", + "Set Develop Values": "Задать значения коррекции", + "Set Flag": "Задать флаг", + "Set Mask Adjustments": "Задать настройки маски", + "Set Point Curve": "Задать точечную кривую", + "Set Profile": "Задать профиль", + "Set Rating": "Задать оценку", + "Set as Album Cover": "Сделать обложкой альбома", + "Set as Before": "Назначить как «до»", + "Set as Reference Photo": "Назначить эталонным фото", + "Set as Target Album": "Назначить целевым альбомом", + "Set as Target Album (B adds to it)": "Назначить целевым альбомом (B добавляет в него)", + "Set as Top of Stack": "Сделать верхним в стопке", + "Set the mix from the photo's colours": "Задать микс по цветам фото", + "Settings": "Настройки", + "Settings new raw photos start from. Changing them doesn't touch photos already in the library.": "Настройки, с которых начинаются новые RAW-фото. Их изменение не затрагивает фото, уже находящиеся в библиотеке.", + "Settings to include": "Включаемые настройки", + "Settings…": "Настройки…", + "Shade": "Тень", + "Shadow": "Тень", + "Shadows": "Тени", + "Shadows Hue": "Тон теней", + "Shadows Luminance": "Яркость теней", + "Shadows Saturation": "Насыщенность теней", + "Shadows Tint": "Оттенок теней", + "Shadows split": "Разделение теней", + "Sharpen": "Повысить резкость", + "Sharpening": "Резкость", + "Sharpness": "Резкость", + "Short Edge": "Короткая сторона", + "Show All": "Показать все", + "Show Clipping": "Показать отсечение", + "Show Deghost Overlay": "Показать наложение удаления артефактов", + "Show Hidden Local Locations": "Показать скрытые места в «Локальном»", + "Show Luminance Map": "Показать карту яркости", + "Show Mask": "Показать маску", + "Show Mask Overlay": "Показать наложение маски", + "Show Mask Pins": "Показать булавки масок", + "Show Original": "Показать оригинал", + "Show Photo Counts": "Показывать количество фото", + "Show Photos with Keyword": "Показать фото с ключевым словом", + "Show Pins": "Показать булавки", + "Show Source": "Показать источник", + "Show Thumbnails": "Показывать миниатюры", + "Show destination gamut warning": "Показывать предупреждение о выходе за гамму устройства вывода", + "Show display gamut warning": "Показывать предупреждение о выходе за гамму дисплея", + "Show file names": "Показывать имена файлов", + "Show in Finder": "Показать в Finder", + "Show on Map": "Показать на карте", + "Show overlay (O)": "Показать наложение (O)", + "Show ratings, flags and edit badges": "Показывать оценки, флаги и значки правок", + "Show the Navigator while zoomed in": "Показывать навигатор при увеличении", + "Show the template tags; click one to insert it": "Показать теги шаблона; щёлкните по тегу, чтобы вставить его", + "Show/Hide Mask": "Показать/скрыть маску", + "Show/hide My Photos panel": "Показать/скрыть панель «Мои фото»", + "Shutter speed": "Выдержка", + "Side panels": "Боковые панели", + "Sidecar names": "Имена XMP-файлов", + "Size": "Размер", + "Size (% of short edge)": "Размер (% от короткой стороны)", + "Size limit": "Ограничение размера", + "Skip": "Пропустить", + "Sky": "Небо", + "Slideshow": "Слайд-шоу", + "Smart Album": "Смарт-альбом", + "Smart Album Rule Fields": "Поля условий смарт-альбома", + "Smart Preview Status": "Состояние смарт-просмотров", + "Smart Previews Location": "Расположение смарт-просмотров", + "Smart previews": "Смарт-просмотры", + "Smoothness": "Плавность", + "Soft B&W": "Мягкое Ч/Б", + "Soft Pastel": "Мягкий пастельный", + "Soft Proofing": "Экранная цветопроба", + "Soft Skin": "Мягкая кожа", + "Sort": "Сортировка", + "Sort by": "Сортировать по", + "Source": "Источник", + "Source code on GitHub": "Исходный код на GitHub", + "Spherical": "Сферическая", + "Spiral": "Спираль", + "Split Stack": "Разделить стопку", + "Spring Fresh": "Весенняя свежесть", + "Square Grid": "Квадратная сетка", + "Square Grid (G toggles)": "Квадратная сетка (G переключает)", + "Square Grid: show file names and formats": "Квадратная сетка: показывать имена и форматы файлов", + "Stack": "Стопка", + "Stack photos taken within this time of each other:": "Объединять в стопку фото, снятые с интервалом не более:", + "Stacks": "Стопки", + "Standard": "Стандартный", + "Start at": "Начать с", + "Start number": "Начальный номер", + "Startup": "Запуск", + "State / Province": "Область / регион", + "Stop": "Остановить", + "Stop Building Previews": "Остановить создание просмотров", + "Stop Using as Target Album": "Больше не использовать как целевой альбом", + "Stopping…": "Остановка…", + "Straighten": "Выровнять", + "Straighten Tool": "Инструмент «Выпрямление»", + "Strong Contrast": "Сильный контраст", + "Style": "Стиль", + "Subfolder": "Подпапка", + "Subfolder name": "Название подпапки", + "Subject": "Главный объект", + "Subtract": "Вычесть", + "Subtract from this mask": "Вычесть из этой маски", + "Suggestions": "Подсказки", + "Summer Haze": "Летняя дымка", + "Sunday": "Воскресенье", + "Survey": "Обзор", + "Survey (N)": "Обзор (N)", + "Swap": "Поменять", + "Swap Aspect (X)": "Поменять пропорции (X)", + "Swap Before and After Settings": "Поменять местами настройки «до» и «после»", + "Swap Compare Photos": "Поменять местами фото для сравнения", + "Switch light / dark theme": "Переключить светлую/тёмную тему", + "Sync Settings": "Синхронизировать настройки", + "System Info": "Сведения о системе", + "System Info…": "Сведения о системе…", + "System default": "Как в системе", + "Tags": "Теги", + "Tags: see Tags beside the template. Files are renamed on disk (with their XMP sidecars); existing names get -1, -2…": "Теги: см. «Теги» рядом с шаблоном. Файлы переименовываются на диске (вместе с XMP-файлами); к существующим именам добавляется -1, -2…", + "Take a snapshot": "Сделать снимок", + "Targeted Adjustment": "Целевая коррекция", + "Targeted adjustment: drag up/down on the photo to adjust the colours there": "Целевая коррекция: перетаскивайте вверх/вниз по фото, чтобы менять цвета в этом месте", + "Targeted adjustment: drag up/down on the photo to adjust the curve there": "Целевая коррекция: перетаскивайте вверх/вниз по фото, чтобы менять кривую в этом месте", + "Teal & Amber": "Бирюзовый и янтарный", + "Teal & Orange": "Бирюзовый и оранжевый", + "Temp": "Темп.", + "Temperature": "Температура", + "Template": "Шаблон", + "Temporary session — your library isn't open, and nothing you do here is saved.": "Временный сеанс — ваша библиотека не открыта, и ничего из сделанного здесь не сохраняется.", + "Text": "Текст", + "Text direction": "Направление текста", + "Texture": "Текстура", + "The SAM 3 model is installed: Object and Describe masks are ready.": "Модель SAM 3 установлена: маски «Объект» и «Описать» готовы.", + "The download didn't work:": "Загрузка не удалась:", + "The original files, unchanged, each with an XMP sidecar holding its edits.": "Исходные файлы без изменений, у каждого — XMP-файл с его правками.", + "The photo has these settings": "У фото есть эти настройки", + "Theme": "Тема", + "They can be restored from Recently Deleted until it is emptied.": "Их можно восстановить из «Недавно удалённых», пока этот раздел не очищен.", + "Thirds": "Правило третей", + "This folder is not available (drive disconnected?). Smart previews are not built or used until it is back or you choose another folder.": "Эта папка недоступна (диск отключён?). Смарт-просмотры не создаются и не используются, пока она не появится снова или вы не выберете другую папку.", + "This photo can't be opened": "Это фото не удаётся открыть", + "Threshold": "Порог", + "Thumbnail cache": "Кэш миниатюр", + "Thursday": "Четверг", + "Tint": "Оттенок", + "Title": "Заголовок", + "Toggle Keyword from Set": "Переключить ключевое слово из набора", + "Toggle Section": "Свернуть/развернуть раздел", + "Toggle Zoom": "Переключить масштаб", + "Tone Curve": "Тоновая кривая", + "Tools": "Инструменты", + "Top Level": "Верхний уровень", + "Transfer": "Перенос", + "Treatment": "Обработка", + "Triangle": "Треугольник", + "Try Again": "Повторить попытку", + "Tuesday": "Вторник", + "Tungsten": "Лампа накаливания", + "Turn Off": "Выключить", + "Turn on Include subfolders, or pick another folder under Local": "Включите «Включая вложенные папки» или выберите другую папку в «Локальном»", + "Type": "Тип", + "Uncheck All": "Снять все отметки", + "Undo": "Отменить", + "Unedited": "Без правок", + "Unflag": "Снять флаг", + "Unflagged": "Без флага", + "Ungroup Stack": "Разгруппировать стопку", + "Unknown Date": "Дата неизвестна", + "Unrated": "Без оценки", + "Unreadable": "Не читается", + "Update": "Обновить", + "Update Mask Shape": "Обновить форму маски", + "Update Preset with Current Settings": "Обновить пресет текущими настройками", + "Update Rules from Current Filter": "Обновить условия из текущего фильтра", + "Update Version": "Обновить версию", + "Update Version with Current Settings": "Обновить версию текущими настройками", + "Update with Current Settings": "Обновить текущими настройками", + "Upright": "Выравнивание (Upright)", + "Urban": "Урбан", + "Use Keyword Set": "Использовать набор ключевых слов", + "Use Library Folder": "Использовать папку библиотеки", + "Use camera-specific defaults": "Использовать настройки для конкретной камеры", + "Use the GPU for rendering": "Использовать GPU для рендеринга", + "User Presets": "Пользовательские пресеты", + "Value": "Значение", + "Variance": "Разброс", + "Version": "Версия", + "Version name": "Название версии", + "Versions": "Версии", + "Versions (Shift+V)": "Версии (Shift+V)", + "Vertical": "По вертикали", + "Vertical text": "Вертикальный текст", + "Vibrance": "Красочность", + "Videos": "Видео", + "View": "Вид", + "Vignette": "Виньетка", + "Vignetting": "Виньетирование", + "Vintage": "Винтаж", + "Virtual Copy": "Виртуальная копия", + "Visible": "Видимость", + "Visualize Spots": "Визуализировать пятна", + "Visualize Spots (A)": "Визуализировать пятна (A)", + "Visualize range": "Показать диапазон", + "Vivid": "Яркий", + "Vivid Pop": "Яркий поп", + "Warm Film": "Тёплая плёнка", + "Warm Glow": "Тёплое сияние", + "Warm Print": "Тёплый отпечаток", + "Warm Table": "Тёплый стол", + "Watched folder": "Отслеживаемая папка", + "Watermark": "Водяной знак", + "Wednesday": "Среда", + "What's New": "Что нового", + "When rated or hovered": "При оценке или наведении", + "White Balance": "Баланс белого", + "White Balance Selector": "Пипетка баланса белого", + "White Balance Selector (W)": "Пипетка баланса белого (W)", + "Whites": "Белые", + "Width": "Ширина", + "Width (% of photo)": "Ширина (% от фото)", + "Width (px)": "Ширина (px)", + "Width × Height": "Ширина × высота", + "Window": "Окно", + "Winter Blue": "Зимняя синева", + "XMP Sidecar Preferences": "Настройки XMP-файлов", + "XMP sidecars": "XMP-файлы", + "Year": "Год", + "Yellow": "Жёлтый", + "Yellow Hue": "Тон жёлтого", + "Yellow Luminance": "Яркость жёлтого", + "Yellow Saturation": "Насыщенность жёлтого", + "Yes": "Да", + "You can close this: the download continues, and resumes if interrupted.": "Это окно можно закрыть: загрузка продолжится и возобновится после обрыва.", + "Your library couldn't be opened": "Не удалось открыть вашу библиотеку", + "Zoom": "Масштаб", + "Zoom 100%": "Масштаб 100%", + "Zoom In": "Увеличить", + "Zoom Out": "Уменьшить", + "Zoom to Fit": "Масштаб по размеру окна", + "Zoom to page level": "Масштаб до уровня страницы", + "and": "и", + "comma, separated": "через запятую", + "memory only": "только в памяти", + "none": "нет", + "not available here": "здесь недоступно", + "of the following rules:": "из следующих условий:", + "saved in": "сохранено в", + "this photo": "это фото", + "− harder border · + softer border": "− жёстче край · + мягче край" +} From 4b57d5dc5a111f761e5a7abf0e1ae6cf6baebe3f Mon Sep 17 00:00:00 2001 From: Daniel Voshart <205798369+voshart@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:54:52 -0400 Subject: [PATCH 007/213] Fetch: a bearer token is sent only when the caller names its variable Options::token_env (default none) replaces the hard-coded LIGHTCRAFT_SAM3_TOKEN, so a download of one model can never send another model's token to its host. SAM 3 names its variable through lightcraft_segment::fetch::options(). The decision (non-empty, https, the host the download began at) is a small function with its own test. Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/segment/mod.rs | 2 +- crates/fetch/src/lib.rs | 19 ++++++++++++------- crates/fetch/src/tests.rs | 17 ++++++++++++++++- crates/segment/src/fetch/mod.rs | 8 ++++++++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/crates/engine/src/segment/mod.rs b/crates/engine/src/segment/mod.rs index 3cf14712e..75e3d11b7 100644 --- a/crates/engine/src/segment/mod.rs +++ b/crates/engine/src/segment/mod.rs @@ -262,7 +262,7 @@ impl Segmenter { if mirrors.is_empty() { return Err(lightcraft_segment::fetch::no_mirrors_message()); } - self.download.start(lightcraft_segment::fetch::SAM3_FILES, mirrors, dir, lightcraft_segment::fetch::Options::default()) + self.download.start(lightcraft_segment::fetch::SAM3_FILES, mirrors, dir, lightcraft_segment::fetch::options()) } #[cfg(not(feature = "sam"))] { diff --git a/crates/fetch/src/lib.rs b/crates/fetch/src/lib.rs index 499a6eda3..12ad639c7 100644 --- a/crates/fetch/src/lib.rs +++ b/crates/fetch/src/lib.rs @@ -79,11 +79,14 @@ pub struct Options { pub stall_timeout: Duration, /// Tries per mirror and file (each resumes where the last one stopped). pub attempts: u32, + /// The environment variable holding a bearer token for the first mirror's host (sent only over https, never to a + /// host a redirect leads to). `None`: nothing is ever sent, so one model's token cannot leak to another's server. + pub token_env: Option<&'static str>, } impl Default for Options { fn default() -> Self { - Options { connect_timeout: Duration::from_secs(15), stall_timeout: Duration::from_secs(30), attempts: 3 } + Options { connect_timeout: Duration::from_secs(15), stall_timeout: Duration::from_secs(30), attempts: 3, token_env: None } } } @@ -203,6 +206,12 @@ pub fn download( Ok(()) } +/// The bearer token to send to `url`: only a non-empty one, only over https, and only to the host the download began +/// at (`origin`), never to the host a redirect leads to. +fn bearer<'a>(token: Option<&'a str>, url: &Url, origin: &str) -> Option<&'a str> { + token.map(str::trim).filter(|t| !t.is_empty() && url.tls && url.host == origin) +} + /// A mirror for messages (no query string: it may hold a token). fn short(m: &str) -> String { Url::parse(m).map(|u| u.display()).unwrap_or_else(|_| "mirror".into()) @@ -268,12 +277,8 @@ fn fetch_file(f: &FileSpec, url: &str, dir: &Path, opts: &Options, cancel: &Atom headers.push(("Range", format!("bytes={have}-"))); } // a token for the configured host only (never sent on to a redirect's host) - if let Ok(t) = std::env::var("LIGHTCRAFT_SAM3_TOKEN") - && !t.trim().is_empty() - && url.tls - && url.host == origin - { - headers.push(("Authorization", format!("Bearer {}", t.trim()))); + if let Some(t) = bearer(opts.token_env.and_then(|name| std::env::var(name).ok()).as_deref(), &url, &origin) { + headers.push(("Authorization", format!("Bearer {t}"))); } let r = http::get(&url, &headers, &limits).map_err(http_fail)?; match r.status { diff --git a/crates/fetch/src/tests.rs b/crates/fetch/src/tests.rs index 57c168a2d..ad3f1063d 100644 --- a/crates/fetch/src/tests.rs +++ b/crates/fetch/src/tests.rs @@ -137,7 +137,7 @@ fn tmp(name: &str) -> PathBuf { } fn quick() -> Options { - Options { connect_timeout: Duration::from_secs(2), stall_timeout: Duration::from_millis(600), attempts: 2 } + Options { connect_timeout: Duration::from_secs(2), stall_timeout: Duration::from_millis(600), attempts: 2, token_env: None } } fn run(files: &[FileSpec], mirrors: &[String], dir: &Path, cancel: &AtomicBool) -> (Result<(), DownloadError>, Vec) { @@ -314,3 +314,18 @@ fn content_ranges() { assert_eq!(parse_content_range("bytes 9-1/300"), None); assert_eq!(parse_content_range("items 1-2/3"), None); } + +/// A bearer token goes only to https, only to the host the download began at, and only when there is one. +#[test] +fn a_token_is_sent_only_over_https_to_the_first_host() { + let u = |s: &str| Url::parse(s).unwrap(); + let first = u("https://huggingface.co/x/model.bin"); + assert_eq!(bearer(Some(" hf_abc \n"), &first, "huggingface.co"), Some("hf_abc")); + // the storage host a redirect leads to, plain http, no token, an empty one + assert_eq!(bearer(Some("hf_abc"), &u("https://cdn-lfs.example/x"), "huggingface.co"), None); + assert_eq!(bearer(Some("hf_abc"), &u("http://huggingface.co/x"), "huggingface.co"), None); + assert_eq!(bearer(None, &first, "huggingface.co"), None); + assert_eq!(bearer(Some(" "), &first, "huggingface.co"), None); + // a caller that names no variable sends nothing, whatever the environment holds + assert!(Options::default().token_env.is_none()); +} diff --git a/crates/segment/src/fetch/mod.rs b/crates/segment/src/fetch/mod.rs index 55c083ccf..35f2d2eb5 100644 --- a/crates/segment/src/fetch/mod.rs +++ b/crates/segment/src/fetch/mod.rs @@ -51,6 +51,14 @@ pub fn mirrors(env: Option<&str>, file: Option<&Path>) -> Vec { lightcraft_fetch::mirrors(env, file, DEFAULT_MIRRORS) } +/// Download settings for SAM 3: the defaults, plus [`TOKEN_ENV`] for a gated mirror. +pub fn options() -> Options { + Options { token_env: Some(TOKEN_ENV), ..Options::default() } +} + +/// Environment variable with a bearer token for the SAM 3 mirror's own host (a gated Hugging Face repository). +pub const TOKEN_ENV: &str = "LIGHTCRAFT_SAM3_TOKEN"; + /// What to tell the user when there is no mirror for the SAM 3 model. pub fn no_mirrors_message() -> String { format!( From 8dd3afce51d27d2625f8e094e6dd11b713cede01 Mon Sep 17 00:00:00 2001 From: Daniel Voshart <205798369+voshart@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:59:22 -0400 Subject: [PATCH 008/213] Fetch: validate caller-supplied names, sizes and hashes; FileSpec borrows its strings; download_url for an exact address The fetch crate is about to serve downloads whose file names, sizes and addresses come from a user-editable list, so it checks them before touching the disk: an ordinary, unique file name (no separators, drive or stream syntax, control characters, trailing dots, Windows device names or .part suffix), a non-zero size within the caller's bound, a well-formed SHA-256, bounded file, mirror and attempt counts, and URLs capped at 16 KiB. FileSpec now borrows its strings (callers with static names are unchanged) so a name read from a file needs no leaking. download_url fetches one file from an exact address, query string included, for signed links. Co-Authored-By: Claude Opus 5.5 --- crates/fetch/README.md | 7 +++ crates/fetch/src/http.rs | 11 ++++- crates/fetch/src/lib.rs | 94 +++++++++++++++++++++++++++++++++------ crates/fetch/src/tests.rs | 61 ++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 crates/fetch/README.md diff --git a/crates/fetch/README.md b/crates/fetch/README.md new file mode 100644 index 000000000..60ac1d329 --- /dev/null +++ b/crates/fetch/README.md @@ -0,0 +1,7 @@ +# lightcraft-fetch + +Native model downloads in pure Rust (L0, Apache-2.0): HTTP/1.1 over std sockets and rustls with the RustCrypto provider, without ring, aws-lc, C or assembly build dependencies. The wasm32 crate is empty. + +Call `download` with bounded file specifications and ordered mirrors, or `download_url` for an exact catalog URL (including its query). Each file has a local filename independent of the URL, a size cap and optional pinned size/SHA-256. Transfers resume pinned `.part` files, verify before renaming, report progress and check cancellation during reads. Run on a worker thread. No proxy support; DNS resolution uses the platform resolver. Tokens are off by default and never forwarded to a different redirect host. + +Tests use local synthetic HTTP servers for redirects, resume, truncation, cancellation, stalls, hashes, size limits and hostile specifications; they make no internet requests. diff --git a/crates/fetch/src/http.rs b/crates/fetch/src/http.rs index 332f01477..e900850ac 100644 --- a/crates/fetch/src/http.rs +++ b/crates/fetch/src/http.rs @@ -60,7 +60,10 @@ pub struct Url { impl Url { pub fn parse(s: &str) -> Result { - let bad = |why: &str| HttpError::BadUrl(format!("{s}: {why}")); + let bad = |why: &str| HttpError::BadUrl(why.to_string()); + if s.len() > 16 * 1024 { + return Err(bad("URL exceeds 16 KiB")); + } let s = s.trim(); if s.chars().any(|c| c.is_control() || c == ' ') { return Err(bad("contains spaces or control characters")); @@ -509,6 +512,12 @@ pub fn get(url: &Url, headers: &[(&str, String)], limits: &Limits) -> Result { + pub name: &'a str, /// Exact size in bytes, when pinned (then the download resumes and is checked against it). pub size: Option, /// SHA-256 (lower-case hex), when pinned. - pub sha256: Option<&'static str>, + pub sha256: Option<&'a str>, /// Largest size accepted when `size` is not pinned. pub max: u64, } @@ -106,6 +106,8 @@ pub struct Progress { pub enum DownloadError { /// No mirror is configured. NoMirrors, + /// Invalid filename, size, hash or aggregate bound supplied by the caller. + Invalid(String), Cancelled, /// A local file problem (folder not writable, disk full…). Disk(String), @@ -119,6 +121,7 @@ pub enum DownloadError { impl std::fmt::Display for DownloadError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + DownloadError::Invalid(e) => write!(f, "invalid download: {e}"), DownloadError::NoMirrors => write!(f, "no download location is configured for this model in this build"), DownloadError::Cancelled => write!(f, "the download was cancelled"), DownloadError::Disk(e) => write!(f, "{e}"), @@ -135,16 +138,45 @@ enum Fail { Disk(String), } -/// Download `files` into `dir` from `mirrors` (in order; see [`mirrors`]). Files already there -/// and verified are kept. `progress` is called as bytes arrive (often: throttle in it). +/// Fetch a single file from its exact URL, preserving any query string. The local filename +/// remains `file.name`; it never comes from redirects or response headers. +pub fn download_url( + file: &FileSpec<'_>, + url: &str, + dir: &Path, + opts: &Options, + cancel: &AtomicBool, + progress: &mut dyn FnMut(&Progress), +) -> Result<(), DownloadError> { + download_source(std::slice::from_ref(file), &[url.to_string()], dir, opts, cancel, progress, true) +} + +/// Download `files` into `dir` from `mirrors` (in order; see [`mirrors`]). Verified files +/// are kept. `progress` is called as bytes arrive (often: throttle in it). pub fn download( - files: &[FileSpec], + files: &[FileSpec<'_>], mirrors: &[String], dir: &Path, opts: &Options, cancel: &AtomicBool, progress: &mut dyn FnMut(&Progress), ) -> Result<(), DownloadError> { + download_source(files, mirrors, dir, opts, cancel, progress, false) +} + +fn download_source( + files: &[FileSpec<'_>], + mirrors: &[String], + dir: &Path, + opts: &Options, + cancel: &AtomicBool, + progress: &mut dyn FnMut(&Progress), + exact_urls: bool, +) -> Result<(), DownloadError> { + let total = validate_files(files)?; + if mirrors.len() > MAX_MIRRORS { + return Err(DownloadError::Invalid("too many mirrors".into())); + } std::fs::create_dir_all(dir).map_err(|e| DownloadError::Disk(format!("can't create {}: {e}", dir.display())))?; // what's left to fetch let mut todo = Vec::new(); @@ -159,14 +191,13 @@ pub fn download( if mirrors.is_empty() { return Err(DownloadError::NoMirrors); } - let total: u64 = files.iter().map(|f| f.size.unwrap_or(0)).sum(); let mut before: u64 = files.iter().filter(|f| !todo.contains(f)).filter_map(|f| f.size).sum(); for f in todo { let mut errors = Vec::new(); let mut ok = false; 'mirrors: for m in mirrors { - let url = format!("{m}/{}", f.name); - for _ in 0..opts.attempts.max(1) { + let url = if exact_urls { m.clone() } else { format!("{m}/{}", f.name) }; + for _ in 0..opts.attempts.clamp(1, 16) { if cancel.load(Ordering::Relaxed) { return Err(DownloadError::Cancelled); } @@ -187,11 +218,11 @@ pub fn download( Err(Fail::Cancelled) => return Err(DownloadError::Cancelled), Err(Fail::Disk(e)) => return Err(DownloadError::Disk(e)), Err(Fail::Retry(e)) => { - log::warn!("download of {url}: {e} (retrying)"); + log::warn!("download of {}: {e} (retrying)", short(&url)); errors.push(format!("{}: {e}", short(m))); } Err(Fail::NextMirror(e)) => { - log::warn!("download of {url}: {e}"); + log::warn!("download of {}: {e}", short(&url)); errors.push(format!("{}: {e}", short(m))); continue 'mirrors; } @@ -206,6 +237,43 @@ pub fn download( Ok(()) } +/// Validate before touching disk, including names supplied by a user model catalog. +fn validate_files(files: &[FileSpec<'_>]) -> Result { + if files.len() > 256 { + return Err(DownloadError::Invalid("too many files".into())); + } + let mut seen = std::collections::HashSet::new(); + let mut total = 0u64; + for f in files { + let stem = f.name.split('.').next().unwrap_or_default().to_ascii_uppercase(); + let reserved = matches!(stem.as_str(), "CON" | "PRN" | "AUX" | "NUL") + || stem + .strip_prefix("COM") + .or_else(|| stem.strip_prefix("LPT")) + .is_some_and(|n| matches!(n, "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9")); + if reserved + || f.name.ends_with(['.', ' ']) + || f.name.ends_with(".part") + || f.name.is_empty() + || f.name.len() > 255 + || f.name.starts_with('.') + || f.name.contains(['/', '\\', ':']) + || f.name.chars().any(char::is_control) + || !seen.insert(f.name) + { + return Err(DownloadError::Invalid("a filename must be a unique, ordinary filename".into())); + } + if f.max == 0 + || f.size.is_some_and(|n| n == 0 || n > f.max) + || f.sha256.is_some_and(|h| h.len() != 64 || !h.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))) + { + return Err(DownloadError::Invalid("invalid file size or SHA-256".into())); + } + total = total.checked_add(f.size.unwrap_or(0)).ok_or_else(|| DownloadError::Invalid("total size overflow".into()))?; + } + Ok(total) +} + /// The bearer token to send to `url`: only a non-empty one, only over https, and only to the host the download began /// at (`origin`), never to the host a redirect leads to. fn bearer<'a>(token: Option<&'a str>, url: &Url, origin: &str) -> Option<&'a str> { @@ -218,7 +286,7 @@ fn short(m: &str) -> String { } /// Whether `path` is already the right file (exact size and hash when pinned). -fn verified(f: &FileSpec, path: &Path) -> Result { +fn verified(f: &FileSpec<'_>, path: &Path) -> Result { let Ok(meta) = std::fs::metadata(path) else { return Ok(false) }; if !meta.is_file() || meta.len() == 0 { return Ok(false); @@ -252,7 +320,7 @@ fn part_path(dir: &Path, name: &str) -> PathBuf { /// Fetch one file from one mirror into `.part` (resuming it when its size is pinned), /// check it and move it into place. -fn fetch_file(f: &FileSpec, url: &str, dir: &Path, opts: &Options, cancel: &AtomicBool, report: &mut dyn FnMut(u64, u64)) -> Result<(), Fail> { +fn fetch_file(f: &FileSpec<'_>, url: &str, dir: &Path, opts: &Options, cancel: &AtomicBool, report: &mut dyn FnMut(u64, u64)) -> Result<(), Fail> { let part = part_path(dir, f.name); let limit = f.size.unwrap_or(f.max); let disk = |e: std::io::Error| Fail::Disk(format!("{}: {e}", part.display())); diff --git a/crates/fetch/src/tests.rs b/crates/fetch/src/tests.rs index ad3f1063d..4a828c6d6 100644 --- a/crates/fetch/src/tests.rs +++ b/crates/fetch/src/tests.rs @@ -22,7 +22,7 @@ fn leak(s: String) -> &'static str { Box::leak(s.into_boxed_str()) } -fn pinned() -> FileSpec { +fn pinned() -> FileSpec<'static> { let p = payload(); FileSpec { name: "model.bin", size: Some(p.len() as u64), sha256: Some(leak(sha_hex(&p))), max: p.len() as u64 } } @@ -140,7 +140,7 @@ fn quick() -> Options { Options { connect_timeout: Duration::from_secs(2), stall_timeout: Duration::from_millis(600), attempts: 2, token_env: None } } -fn run(files: &[FileSpec], mirrors: &[String], dir: &Path, cancel: &AtomicBool) -> (Result<(), DownloadError>, Vec) { +fn run(files: &[FileSpec<'_>], mirrors: &[String], dir: &Path, cancel: &AtomicBool) -> (Result<(), DownloadError>, Vec) { let mut seen = Vec::new(); let r = download(files, mirrors, dir, &quick(), cancel, &mut |p| seen.push(p.clone())); (r, seen) @@ -329,3 +329,60 @@ fn a_token_is_sent_only_over_https_to_the_first_host() { // a caller that names no variable sends nothing, whatever the environment holds assert!(Options::default().token_env.is_none()); } + +#[test] +fn exact_url_keeps_query_and_owned_catalog_values() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let url = format!("http://{}/download?file=weights&sig=secret", listener.local_addr().unwrap()); + let worker = std::thread::spawn(move || { + let (mut socket, _) = listener.accept().unwrap(); + let mut reader = BufReader::new(socket.try_clone().unwrap()); + let mut request = String::new(); + reader.read_line(&mut request).unwrap(); + assert_eq!(request.trim(), "GET /download?file=weights&sig=secret HTTP/1.1"); + loop { + let mut line = String::new(); + if reader.read_line(&mut line).unwrap() == 0 || line.trim().is_empty() { + break; + } + } + socket.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 3\r\nConnection: close\r\n\r\nabc").unwrap(); + }); + let name = String::from("catalog.onnx"); + let hash = sha_hex(b"abc"); + let spec = FileSpec { name: &name, size: Some(3), sha256: Some(&hash), max: 3 }; + let dir = tmp("exact"); + let mut progress = Vec::new(); + download_url(&spec, &url, &dir, &quick(), &AtomicBool::new(false), &mut |p| progress.push(p.clone())).unwrap(); + worker.join().unwrap(); + assert_eq!(std::fs::read(dir.join(name)).unwrap(), b"abc"); + assert!(progress.iter().all(|p| !p.mirror.contains("secret"))); + let _ = std::fs::remove_dir_all(dir); +} + +#[test] +fn hostile_file_specs_are_rejected_before_touching_disk() { + let dir = tmp("invalid"); + let base = FileSpec { name: "valid.onnx", size: Some(1), sha256: None, max: 1 }; + let cancel = AtomicBool::new(false); + for name in ["", ".hidden", "../escape", "a/b", "a\\b", "C:escape", "bad\nname", "trailing.", "CON.onnx", "a.part"] { + let spec = FileSpec { name, ..base.clone() }; + assert!(matches!(run(&[spec], &[], &dir, &cancel).0, Err(DownloadError::Invalid(_))), "{name}"); + assert!(!dir.exists()); + } + for spec in [ + FileSpec { max: 0, ..base.clone() }, + FileSpec { size: Some(0), ..base.clone() }, + FileSpec { size: Some(2), ..base.clone() }, + FileSpec { sha256: Some("not a SHA"), ..base.clone() }, + ] { + assert!(matches!(run(&[spec], &[], &dir, &cancel).0, Err(DownloadError::Invalid(_)))); + assert!(!dir.exists()); + } + let huge = FileSpec { size: Some(u64::MAX), max: u64::MAX, ..base.clone() }; + let other = FileSpec { name: "other.onnx", ..huge.clone() }; + for specs in [vec![huge, other], vec![base.clone(); 257], vec![base.clone(), base]] { + assert!(matches!(run(&specs, &[], &dir, &cancel).0, Err(DownloadError::Invalid(_)))); + assert!(!dir.exists()); + } +} From 6c7405ed3e1752e7071bc29c9ba16607bbbf1093 Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 07:06:23 +0400 Subject: [PATCH 009/213] Fix Russian translations in ru.json --- crates/ui-egui/locales/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json index 7948e4c66..b935e1910 100644 --- a/crates/ui-egui/locales/ru.json +++ b/crates/ui-egui/locales/ru.json @@ -4,7 +4,7 @@ "0: nothing is rejected.": "0: ничего не отклоняется.", "A filter is hiding this view's photos: change it, or clear it (View → Clear Filters)": "Фильтр скрывает фото в этом представлении: измените его или сбросьте (Вид → Сбросить фильтры)", "A one-time download of about": "Однократная загрузка около", - "About LightCraft": "О программе LightCraft", + "About LightCraft": "О LightCraft", "Activity": "Активность", "Actual size": "Реальный размер", "Add": "Добавить", @@ -204,7 +204,7 @@ "Comments panel": "Панель комментариев", "Compare": "Сравнение", "Compare (Shift+C)": "Сравнение (Shift+C)", - "Compare Before and After": "Сравнить «до» и «после»", + "Compare Before and After": "Сравнить до и после", "Compare needs at least two photos": "Для сравнения нужно минимум два фото", "Component options": "Параметры компонента", "Compression": "Сжатие", From 4e012a59a6d2442ba749f109ea27aadb5c0b11a5 Mon Sep 17 00:00:00 2001 From: Gardy Date: Wed, 7 Oct 2026 22:10:21 -0600 Subject: [PATCH 010/213] =?UTF-8?q?macOS=20menu:=20single-key=20shortcuts?= =?UTF-8?q?=20reach=20egui=20(E,=20C,=20=E2=87=A7P,=20ratings=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS every shortcut that appears in the menu bar was left to the native menu, and the egui shortcut handler skipped those keys. AppKit only offers a key press to the menu bar when it carries ⌘ or ⌃, or is a function key; a plain key (E, C, H, M, I, K, D, 0–9, P, U, Y…) or one with only ⇧ / ⌥ (⇧P, ⇧V, ⌥Y…) goes to the window's winit view instead. So all of those did nothing on macOS, while keys outside the menus (Space, G, X, ⌫) and ⌘ shortcuts worked. `publish_shortcuts` now lists only the shortcuts the menu bar really receives (`menu_delivers`: ⌘, ⌃ or a function key) in `native_shortcuts`. The others keep their accelerator on the menu item, so the menu still shows the key, and egui runs them. Checked in a release build by pressing keys (not via the control channel, which bypasses the native menu): C opens Crop & Rotate, E Edit, ⇧P Presets, 3 / 0 set and clear the rating; ⌘⇧H toggles the histogram once per press; typing "c" in the search field types and leaves Crop closed. The same build without this change ignored C, ⇧P and 3. ⌃ shortcuts (merges) are unchanged and were not tried. Regression test: native_menu::tests::single_key_shortcuts_stay_with_egui (fails with the previous behaviour: "E must be left to egui"). --- apps/lightcraft/src/native_menu.rs | 64 +++++++++++++++++++++++++----- docs/whats-new.md | 4 ++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/apps/lightcraft/src/native_menu.rs b/apps/lightcraft/src/native_menu.rs index 557c9abfb..45e90718e 100644 --- a/apps/lightcraft/src/native_menu.rs +++ b/apps/lightcraft/src/native_menu.rs @@ -2,9 +2,12 @@ //! the in-window menus ([`lightcraft_ui_egui::menubar`]): the registry's menu paths, shortcuts, //! enabled and checked state, and live labels ("Undo Exposure"). //! -//! Key handling: a native key equivalent consumes the key press before egui sees it, so -//! - shortcuts installed natively are listed in `LightcraftApp::native_shortcuts` and skipped by -//! the egui shortcut handler (nothing fires twice); +//! Key handling: AppKit offers a key press to the menu bar only when it carries ⌘ or ⌃, or is a +//! function key (F1…). Every other key (`E`, `1`, `⇧P`, `⌥Y`…) goes straight to the window, whose +//! winit view always takes it, so a menu item never fires from it. Hence +//! - shortcuts the menu bar really receives ([`menu_delivers`]) are listed in +//! `LightcraftApp::native_shortcuts` and skipped by the egui shortcut handler (nothing fires +//! twice); the others stay on their menu items for display and egui runs them; //! - while a text field has keyboard focus, accelerators without ⌘ (`G`, `1`, `Delete`…) and the //! text-editing ones (⌘A/⌘C/⌘V/⌘X/⌘Z) are removed, so typing and text editing work; they come //! back when the field loses focus. @@ -146,6 +149,18 @@ fn yields_to_text(sc: &str) -> bool { !sc.contains("Cmd") && !sc.contains("Ctrl") || TEXT_EDIT.contains(&sc) } +/// Whether AppKit hands this shortcut to the menu bar: only key presses with ⌘ or ⌃, and function +/// keys. A plain `C` or `⇧P` never reaches it (the window's view takes the key), so egui has to run +/// those even though their menu item shows the key. +fn menu_delivers(sc: &str) -> bool { + sc.split('+').any(|part| matches!(part, "Cmd" | "Ctrl") || part.strip_prefix('F').is_some_and(|n| n.parse::().is_ok())) +} + +/// The shortcuts the menu bar runs itself, for `LightcraftApp::native_shortcuts`. +fn owned_by_menu<'a>(installed: impl Iterator, text_focus: bool) -> HashSet { + installed.filter(|sc| menu_delivers(sc) && !(text_focus && yields_to_text(sc))).map(str::to_string).collect() +} + /// `&` marks a mnemonic in muda labels. fn label_text(s: &str) -> String { lightcraft_ui_egui::i18n::tr(s).replace('&', "&&") @@ -329,13 +344,8 @@ impl NativeMenu { /// Tell the egui shortcut handler which shortcuts the menu bar currently owns. fn publish_shortcuts(&self, app: &mut LightcraftApp) { - app.native_shortcuts = self - .items - .values() - .filter(|i| i.accel.is_some()) - .filter_map(|i| i.shortcut.clone()) - .filter(|sc| !(self.text_focus && yields_to_text(sc))) - .collect::>(); + let installed = self.items.values().filter(|i| i.accel.is_some()).filter_map(|i| i.shortcut.as_deref()); + app.native_shortcuts = owned_by_menu(installed, self.text_focus); app.native_shortcuts.insert(SETTINGS_KEY.to_string()); } @@ -467,4 +477,38 @@ mod tests { assert!(accelerator(sc).is_some(), "{sc}"); } } + + /// Single keys shown in the menu bar (E, C, ⇧P, ratings…) never reach it on macOS, so egui must + /// keep handling them; ⌘ / ⌃ combinations and function keys are the menu bar's own. + #[test] + fn single_key_shortcuts_stay_with_egui() { + assert!(menu_delivers("Cmd+Shift+H") && menu_delivers("Ctrl+H") && menu_delivers("F2") && menu_delivers("Cmd+F11")); + assert!(!menu_delivers("E") && !menu_delivers("Shift+P") && !menu_delivers("Alt+Y") && !menu_delivers("1") && !menu_delivers("F")); + let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Default::default()); + fn all(n: &[MenuNode], out: &mut Vec) { + for x in n { + match x { + MenuNode::Item { shortcut: Some(s), .. } if accelerator(s).is_some() => out.push(s.clone()), + MenuNode::Submenu { children, .. } => all(children, out), + _ => {} + } + } + } + let mut installed = Vec::new(); + for (_, v) in menu_bar(&app) { + all(&v, &mut installed); + } + for sc in ["E", "C", "Shift+P", "Cmd+Shift+H", "F2"] { + assert!(installed.iter().any(|s| s == sc), "{sc} is in the menu bar"); + } + let owned = owned_by_menu(installed.iter().map(String::as_str), false); + for sc in ["E", "C", "Shift+P"] { + assert!(!owned.contains(sc), "{sc} must be left to egui"); + } + for sc in ["Cmd+Shift+H", "F2"] { + assert!(owned.contains(sc), "{sc} is run by the menu bar"); + } + // while typing, the menu bar gives up F2 too (the text field has it) + assert!(!owned_by_menu(installed.iter().map(String::as_str), true).contains("F2")); + } } diff --git a/docs/whats-new.md b/docs/whats-new.md index ea0243f42..6fa6454f6 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -14,6 +14,10 @@ - 23 new built-in presets: Portrait, Landscape, Urban, Food, Seasons, Vintage and B&W toners. ### Reliability +- On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5, + labels 6–9, P / U and the rest did nothing, because macOS only passes ⌘ / ⌃ combinations and function keys + to the menu bar and the app ignored those keys, assuming the menu bar would handle them. Keys outside the menus + (Space, G, X, ⌫) and ⌘ shortcuts were not affected. - LightCraft no longer crashes at launch on Windows PCs whose Vulkan driver is broken (issue #136, e.g. some Intel UHD 630 drivers): on Windows the window and GPU rendering use DirectX 12 only and never load the Vulkan driver unless asked to. `LIGHTCRAFT_GPU_BACKEND=dx12 | vulkan | metal | off` (or wgpu's `WGPU_BACKEND`, which GPU rendering From 03bc1469f9993a3e2540e21fd4d1ffeb14dcc636 Mon Sep 17 00:00:00 2001 From: radialmonster Date: Wed, 7 Oct 2026 23:57:26 -0400 Subject: [PATCH 011/213] LR-EDIT-OPTICS-PROFILE: apply the distortion correction Panasonic/Leica record in RW2/RWL (#256) The RW2 reader reads PanasonicRaw tag 0x0119 (16 LE words) and expresses the camera's own correction as the equivalent DNG WarpRectilinear in OpcodeList3, so embedded_lens() turns it into the existing EmbeddedLens/EmbeddedWarp and "Enable Profile Corrections" applies it like a DNG's warp (no new warp path; Convert to DNG keeps it). Rule, from our own measurements against the embedded camera JPEGs (clean-room; ExifTool tag names only): corrected = rho*(1 + (w8 rho^2 + w4 rho^4 + w11 rho^6)/32768), rho = r/w12 (w12 = active-area half diagonal), output = corrected / (1 + w5/32768), on when (w7 & 0xf) == 1. Chosen on 38 GX85 photos, held out on 35 photos from 12 other bodies / 9 other lenses; corrected renders line up with the camera JPEG within 0.75 px at 1440 px at the corners (up to 13 % of the half diagonal before). Implausible values leave the photo uncorrected. The loader now removes lens opcodes before the RW2 camera-look fit (its binned proxy needs an empty OpcodeList3); Opcode::is_lens_correction() replaces the duplicated matches. RENDER_CACHE_VERSION 11 -> 12. --- README.md | 2 +- crates/develop/src/settings.rs | 5 +- crates/engine/examples/render_bench.rs | 4 +- crates/engine/src/camera_profiles.rs | 4 +- crates/engine/src/files.rs | 141 ++++++++++++++-- crates/engine/src/media.rs | 2 +- crates/merge/src/frame.rs | 4 +- crates/pipeline/src/optics.rs | 3 +- crates/raw/src/opcodes.rs | 7 + crates/raw/src/vendor/rw2.rs | 216 ++++++++++++++++++++++++- docs/camera-preview-colour.md | 2 +- docs/parity.md | 5 +- docs/whats-new.md | 5 + 13 files changed, 376 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index f1f2e4bc8..8ed09b89d 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,7 @@ LightCraft is young and moving fast. **Where we honestly stand** (details in the | Import: Add in place / Copy / Move, rename and folder templates, devices, duplicate detection, watched folders; Local folder browsing | ✅ | | MCP server (headless or live app, persistent libraries), CLI, control channel | ✅ | | XMP sidecars (read/write, auto-write), reading `crs:` develop settings, preset files (`.lcpreset`, XMP presets) | ✅ | -| Optics (distortion, vignetting, auto + manual CA, defringe, DNG-embedded lens corrections), Geometry (transforms, Constrain Crop), Upright (Auto/Level/Vertical/Full/Guided) | ✅ · camera lens profiles (our own) ⬜ | +| Optics (distortion, vignetting, auto + manual CA, defringe, lens corrections embedded in DNG files and Panasonic / Leica RW2 / RWL distortion data), Geometry (transforms, Constrain Crop), Upright (Auto/Level/Vertical/Full/Guided) | ✅ · camera lens profiles (our own) ⬜ | | Photo Merge: HDR (auto-align, deghost), Panorama (spherical/cylindrical/perspective, boundary warp, auto crop), HDR Panorama → DNG | ✅ | | GPU pipeline (wgpu compute, CPU-exact within 1/255), CPU fallback on device limits / errors | ✅ · WebGPU in the browser 🚧 | | AI: segmentation masks, AI denoise, super resolution, faces; HDR editing; video | ⬜ (see [roadmap](ROADMAP.md#where-were-going)) | diff --git a/crates/develop/src/settings.rs b/crates/develop/src/settings.rs index 800f229b5..29e23d012 100644 --- a/crates/develop/src/settings.rs +++ b/crates/develop/src/settings.rs @@ -539,8 +539,9 @@ impl Default for Optics { } } -/// Lens corrections embedded in a DNG file (`OpcodeList3`: `WarpRectilinear`, `FixVignetteRadial`), converted -/// to the oriented, default-cropped image. This is camera/file data (stored on the photo record, not in the develop +/// Lens corrections embedded in the file, converted to the oriented, default-cropped image: a DNG's `OpcodeList3` +/// (`WarpRectilinear`, `FixVignetteRadial`), or a raw reader's equivalent of the camera's own correction (Panasonic / +/// Leica RW2 distortion, `lightcraft_raw`'s `vendor/rw2.rs`). This is camera/file data (stored on the photo record, not in the develop /// settings); "Enable Profile Corrections" applies it, scaled by the profile distortion/vignetting amounts. /// LightCraft never uses Adobe LCP lens profiles. #[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] diff --git a/crates/engine/examples/render_bench.rs b/crates/engine/examples/render_bench.rs index ac6a9a60d..55dc2b11d 100644 --- a/crates/engine/examples/render_bench.rs +++ b/crates/engine/examples/render_bench.rs @@ -93,7 +93,9 @@ fn main() { for path in &args { let bytes = std::fs::read(path).expect("read"); println!("{path}"); - let raw = lightcraft_raw::decode(&bytes).expect("decode"); + let mut raw = lightcraft_raw::decode(&bytes).expect("decode"); + // as the loader does: lens corrections are applied by the pipeline, not in the raw develop + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); println!(" {}×{} cpp {} cfa {:?}", raw.width, raw.height, raw.cpp, raw.cfa.as_ref().map(|c| c.name())); println!(" {:<36} {}", "raw decode:", best(n, || drop(lightcraft_raw::decode(&bytes).expect("decode")))); println!(" {:<36} {}", "normalize:", best(n, || drop(raw.normalized().expect("norm")))); diff --git a/crates/engine/src/camera_profiles.rs b/crates/engine/src/camera_profiles.rs index 29efa4b88..3d5117e56 100644 --- a/crates/engine/src/camera_profiles.rs +++ b/crates/engine/src/camera_profiles.rs @@ -199,7 +199,9 @@ impl Pool { /// Add one raw file's colour pairs. `Ok(None)` when the file can't contribute (not an ARW or /// NEF without colour matrices, no usable camera JPEG, too little colour). pub fn add(&mut self, bytes: &[u8]) -> Result, String> { - let raw = lightcraft_raw::decode(bytes).map_err(|e| e.to_string())?; + let mut raw = lightcraft_raw::decode(bytes).map_err(|e| e.to_string())?; + // colour only: geometric lens corrections would stop the sensor proxy from binning + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); let Some(model) = raw.metadata.model.as_deref().map(str::trim).filter(|m| !m.is_empty()) else { return Ok(None) }; let Some(pairs) = crate::camera_preview::profile_pairs(&raw, bytes) else { return Ok(None) }; let step = pairs.len().div_ceil(PAIRS_PER_FILE).max(1); diff --git a/crates/engine/src/files.rs b/crates/engine/src/files.rs index 62a1c467f..26f970e28 100644 --- a/crates/engine/src/files.rs +++ b/crates/engine/src/files.rs @@ -43,8 +43,9 @@ fn meta_of(m: &lightcraft_meta::Metadata) -> (Meta, Option) { (meta, m.capture_time.as_ref().map(|d| d.to_iso())) } -/// Lens corrections embedded in a DNG's `OpcodeList3` (`WarpRectilinear`, `FixVignetteRadial`), re-expressed for -/// the default-cropped, EXIF-oriented image. These are the only "profile" corrections LightCraft applies. +/// Lens corrections embedded in a raw's `OpcodeList3` (`WarpRectilinear`, `FixVignetteRadial`: a DNG's own, or the +/// raw reader's equivalent of the camera's correction, e.g. Panasonic / Leica RW2 distortion), re-expressed for the +/// default-cropped, EXIF-oriented image. These are the only "profile" corrections LightCraft applies. pub fn embedded_lens(raw: &lightcraft_raw::RawInfo) -> Option { use lightcraft_develop::{EmbeddedLens, EmbeddedVignette, EmbeddedWarp}; use lightcraft_geom::Point; @@ -87,10 +88,6 @@ pub fn embedded_lens(raw: &lightcraft_raw::RawInfo) -> Option bool { - matches!(op, lightcraft_raw::Opcode::WarpRectilinear { .. } | lightcraft_raw::Opcode::FixVignetteRadial { .. }) -} - fn ext_upper(name: &str) -> String { std::path::Path::new(name).extension().map(|e| e.to_string_lossy().to_uppercase()).unwrap_or_default() } @@ -224,13 +221,14 @@ fn load_bytes_now(bytes: std::borrow::Cow<'_, [u8]>, max_edge: usize) -> Result< } Err(e) => return Err(e.to_string()), }; + // Embedded lens corrections are applied by the pipeline ("Enable Profile Corrections"), not baked in + // (removed before the camera look's binned sensor proxy too, which needs an empty `OpcodeList3`). + let lens = embedded_lens(&raw.info()); + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); let xy = lightcraft_raw::color::as_shot_white_xy(&raw); let t = lightcraft_raw::color::camera_transform(&raw, xy); let camera_look = crate::camera_preview::fit_preview(&raw, &bytes, &t); drop(bytes); - // Embedded lens corrections are applied by the pipeline ("Enable Profile Corrections"), not baked in. - let lens = embedded_lens(&raw.info()); - raw.opcodes.list3.retain(|op| !is_lens_opcode(op)); // Previews and thumbnails bin the mosaic straight to (about) the size they need; only // larger levels (exports, 1:1) demosaic the whole sensor. let t0 = web_time::Instant::now(); @@ -402,6 +400,131 @@ mod tests { use super::*; use lightcraft_codecs::{ChromaSubsampling, EncodeImage, EncodeMeta, Samples, encode_jpeg}; + /// Luminance (from linear RGB) minus its 9 × 9 local mean: tone differences between a render and a camera JPEG + /// mostly cancel, edges remain. + fn detail(w: usize, h: usize, lum: impl Fn(usize, usize) -> f32) -> Vec { + let l: Vec = (0..w * h).map(|i| lum(i % w, i / w)).collect(); + let mut out = vec![0.0; w * h]; + for y in 0..h { + for x in 0..w { + let (x0, x1, y0, y1) = (x.saturating_sub(4), (x + 5).min(w), y.saturating_sub(4), (y + 5).min(h)); + let m = (y0..y1).flat_map(|yy| (x0..x1).map(move |xx| (xx, yy))).map(|(xx, yy)| l[yy * w + xx]).sum::(); + out[y * w + x] = l[y * w + x] - m / ((x1 - x0) * (y1 - y0)) as f32; + } + } + out + } + + /// The integer shift (within ±`r` px) of the `p × p` patch of `a` at `(x, y)` that best matches `b`, with its + /// normalised cross-correlation, or `None` when the patch has too little detail to tell. + fn patch_shift(a: &[f32], b: &[f32], w: usize, (x, y): (usize, usize), p: usize, r: i64) -> Option<((i64, i64), f32)> { + let pa: Vec = (0..p * p).map(|i| a[(y + i / p) * w + x + i % p]).collect(); + let na = pa.iter().map(|v| v * v).sum::().sqrt(); + if na < 1e-3 * p as f32 { + return None; + } + let mut best = (f32::MIN, (0, 0)); + for dy in -r..=r { + for dx in -r..=r { + let (bx, by) = (x as i64 + dx, y as i64 + dy); + let (mut dot, mut nb) = (0.0f32, 0.0f32); + for i in 0..p { + for j in 0..p { + let v = b[(by as usize + i) * w + bx as usize + j]; + dot += pa[i * p + j] * v; + nb += v * v; + } + } + let c = dot / (na * nb.sqrt()).max(1e-12); + if c > best.0 { + best = (c, (dx, dy)); + } + } + } + Some((best.1, best.0)) + } + + /// Issue #256: public Panasonic / Leica raws (skipped without the corpus) are corrected for distortion the way the + /// camera corrected its own JPEG (tag 0x0119): at the corners of the frame, the render with "Enable Profile + /// Corrections" lines up with the embedded JPEG within 2 px at 640 px, while the uncorrected render of the wide + /// lenses is far off. A file shot with the camera's correction off (DMC-GH1) carries no correction. + #[test] + fn corpus_rw2_distortion_matches_the_camera_jpeg() { + let dir = std::env::var_os("LIGHTCRAFT_CORPUS") + .map(std::path::PathBuf::from) + .unwrap_or_else(|| std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus")) + .join("raw"); + let Ok(gh1) = std::fs::read(dir.join("rw2-panasonic-gh1.rw2")) else { + eprintln!("skip: {} absent", dir.display()); + return; + }; + assert_eq!(probe_bytes("gh1.rw2", &gh1).unwrap().embedded_lens, None, "correction off: nothing to apply"); + assert_eq!(load_bytes(&gh1, 320).unwrap().1.lens, None); + + // (file, how far off (px) the uncorrected corners are at the least, if they match within ±R at all) + let files = [ + ("rw2-panasonic-fz1000m2-4x3.rw2", 8), // 9.1 mm, ~13 % at the corners + ("rwl-leica-dlux7.rwl", 8), // 10.9 mm, ~13 % + ("rw2-panasonic-gx80.rw2", 8), // 14 mm, ~8 % + ("rw2-panasonic-gh6.rw2", 3), // 20 mm, ~1.6 % + ("rw2-panasonic-s9.rw2", 3), // 45 mm, 3 % zoom after the correction + ("rw2-panasonic-g9.rw2", 0), // 40 mm, ~0.5 % + ]; + const EDGE: usize = 640; + const P: usize = 64; + const R: i64 = 12; + for (name, off_at_least) in files { + let bytes = std::fs::read(dir.join(name)).unwrap(); + let probed = probe_bytes(name, &bytes).unwrap(); + assert!(probed.embedded_lens.is_some_and(|l| l.warp.is_some()), "{name}: no distortion correction read"); + let (src, info) = load_bytes(&bytes, 2 * EDGE).unwrap(); + assert_eq!(info.lens, probed.embedded_lens, "{name}: probe and decode disagree"); + // Convert to DNG keeps it: the same correction, as the DNG's own `WarpRectilinear` + let dng = lightcraft_raw::write_dng(&lightcraft_raw::decode(&bytes).unwrap(), &Default::default()).unwrap(); + assert_eq!(probe_bytes("x.dng", &dng).unwrap().embedded_lens, probed.embedded_lens, "{name}: lost in the DNG"); + let mut s = lightcraft_develop::DevelopSettings::default(); + let render = |s: &lightcraft_develop::DevelopSettings| { + lightcraft_pipeline::render(&src, &info, s, &lightcraft_pipeline::RenderRequest::fit(EDGE, EDGE)).image + }; + let before = render(&s); + s.optics.lens_profile = true; + let after = render(&s); + let (w, h) = (after.width, after.height); + assert_eq!((before.width, before.height), (w, h)); + // the camera JPEG shows the whole sensor; a render in an in-camera aspect ratio is its centre + let (cam, _) = load_embedded_preview(&bytes, 4 * EDGE).unwrap(); + let (cw, ch) = (cam.width as f64, cam.height as f64); + let (sw, sh) = if cw / ch > w as f64 / h as f64 { (ch * w as f64 / h as f64, ch) } else { (cw, cw * h as f64 / w as f64) }; + let cam = cam.crop(((cw - sw) / 2.0).round() as usize, ((ch - sh) / 2.0).round() as usize, sw.round() as usize, sh.round() as usize); + let cam = lightcraft_raster::resample::resize(&cam, w, h, Filter::Mitchell); + let lin = |v: u8| (v as f32 / 255.0).powf(2.2); + let lum8 = |im: &lightcraft_raster::Rgba8, x: usize, y: usize| { + let p = im.data[y * im.width + x]; + 0.3 * lin(p[0]) + 0.6 * lin(p[1]) + 0.1 * lin(p[2]) + }; + let reference = detail(w, h, |x, y| { + let p = cam.data[y * w + x]; + 0.3 * p[0] + 0.6 * p[1] + 0.1 * p[2] + }); + let (db, da) = (detail(w, h, |x, y| lum8(&before, x, y)), detail(w, h, |x, y| lum8(&after, x, y))); + let m = R as usize + 4; + let corners = [(m, m), (w - m - P, m), (m, h - m - P), (w - m - P, h - m - P)]; + let mut measured = 0; + for c in corners { + let Some(((dx, dy), ca)) = patch_shift(&da, &reference, w, c, P, R) else { continue }; + measured += 1; + assert!(dx.abs() <= 2 && dy.abs() <= 2 && ca >= 0.5, "{name}: corrected corner {c:?} off by ({dx}, {dy}) px, ncc {ca}"); + if off_at_least > 0 + && let Some(((bx, by), cb)) = patch_shift(&db, &reference, w, c, P, R) + { + let off = bx.abs().max(by.abs()) >= off_at_least || cb < 0.3; + assert!(off, "{name}: uncorrected corner {c:?} already within ({bx}, {by}) px, ncc {cb}"); + } + } + assert!(measured >= 2, "{name}: only {measured} corners with detail"); + } + } + /// A CR3-shaped file (not decodable yet) whose only content is a `PRVW` preview box. fn cr3_with_preview(w: u32, h: u32) -> Vec { let px: Vec = (0..w * h).flat_map(|i| [(i % 251) as u8, 128, 200]).collect(); diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a2..7aafbf349 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; diff --git a/crates/merge/src/frame.rs b/crates/merge/src/frame.rs index df016b5d4..360c7424e 100644 --- a/crates/merge/src/frame.rs +++ b/crates/merge/src/frame.rs @@ -111,9 +111,7 @@ pub fn load_frame(bytes: &[u8], max_edge: Option, orient: bool) -> Result if lightcraft_raw::probe(bytes).is_some() { let mut raw = lightcraft_raw::decode(bytes).map_err(|e| MergeError::Decode(e.to_string()))?; // geometric lens corrections stay with the develop settings ("profile corrections") - raw.opcodes - .list3 - .retain(|op| !matches!(op, lightcraft_raw::Opcode::WarpRectilinear { .. } | lightcraft_raw::Opcode::FixVignetteRadial { .. })); + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); let small = max_edge.is_some_and(|m| m <= 1200); let method = if small { lightcraft_raw::Method::Bilinear } else { lightcraft_raw::Method::Ahd }; let mut image = raw.develop(method).map_err(|e| MergeError::Decode(e.to_string()))?; diff --git a/crates/pipeline/src/optics.rs b/crates/pipeline/src/optics.rs index ad789fc87..f99a3cd80 100644 --- a/crates/pipeline/src/optics.rs +++ b/crates/pipeline/src/optics.rs @@ -11,7 +11,8 @@ //! dimensionless scales), so a 400 px preview and a full-size export are warped identically. //! //! Also here: automatic lateral chromatic aberration estimation ([`estimate_lateral_ca`]) and [`defringe`]. -//! Profile corrections use only lens data embedded in DNG files (`WarpRectilinear` / `FixVignetteRadial`). +//! Profile corrections use only lens data embedded in the files: DNG `WarpRectilinear` / `FixVignetteRadial`, and the +//! raw readers' equivalent of a camera's own correction (Panasonic / Leica RW2 distortion). use std::sync::Mutex; diff --git a/crates/raw/src/opcodes.rs b/crates/raw/src/opcodes.rs index f63eaf17e..bca397748 100644 --- a/crates/raw/src/opcodes.rs +++ b/crates/raw/src/opcodes.rs @@ -98,6 +98,13 @@ impl Opcode { pub fn is_applied(&self) -> bool { !matches!(self, Opcode::Unknown { .. } | Opcode::TrimBounds { .. } | Opcode::WarpFisheye { .. }) } + + /// Whether this is a lens correction (`WarpRectilinear`, `FixVignetteRadial`): from a DNG, or the equivalent of + /// a camera's own correction recorded in its raw (Panasonic RW2). Developers that apply these through the + /// "profile corrections" of the develop settings remove them before demosaicing. + pub fn is_lens_correction(&self) -> bool { + matches!(self, Opcode::WarpRectilinear { .. } | Opcode::FixVignetteRadial { .. }) + } } /// The three opcode lists of a DNG raw IFD. diff --git a/crates/raw/src/vendor/rw2.rs b/crates/raw/src/vendor/rw2.rs index 10ade6272..599439ce5 100644 --- a/crates/raw/src/vendor/rw2.rs +++ b/crates/raw/src/vendor/rw2.rs @@ -67,12 +67,38 @@ //! - Active area: the sensor borders. Default crop: `0x002f`–`0x0032`, the in-camera aspect ratio (e.g. 4:3 or 1:1 //! on a 3:2 sensor; the embedded JPEG always shows the whole sensor). Some bodies write zeros there: ignored. //! +//! Lens distortion (tag `0x0119`, ExifTool name DistortionInfo; ExifTool's names for its words were the only outside +//! input, their meaning below is our own measurement). The tag holds 16 little-endian 16-bit words. The camera's +//! own correction, which its embedded JPEG shows, is applied as an `OpcodeList3` `WarpRectilinear` (the DNG form +//! LightCraft already applies under "Enable Profile Corrections"): +//! +//! - Word 7, low 4 bits: 1 = correction on [0 on the DMC-GH1, whose JPEG matches the uncorrected render]. +//! - Word 12: the normalisation radius in pixels, the half diagonal of the active area [2870 for 4592 × 3448 +//! (2871.2), 3240 for 5184 × 3888, 3611 for 5776 × 4336, 3288 for the FZ1000 II's whole 5472 × 3648 sensor +//! even in its 4:3 mode]. +//! - Words 8, 4 and 11 (ExifTool: DistortionParam08, 04, 11), divided by 32768, are `k2`, `k4`, `k6` of the +//! correction: a sensor point at radius ρ (in units of word 12, about the centre of the active area) appears in +//! the corrected image at ρ·(1 + k2 ρ² + k4 ρ⁴ + k6 ρ⁶). +//! - Word 5 (ExifTool: DistortionScale), divided by 32768, plus 1, is the zoom `z` the camera crops by afterwards: +//! output radius = corrected radius / z [z < 1 when the corrected corners would otherwise be empty]. +//! +//! [These rules were chosen among alternative word orders, powers, directions and readings of the zoom by fitting +//! radial curves between our uncorrected renders and the embedded JPEGs of 38 DMC-GX85 photos (12–32 mm and 25 mm +//! lenses). With no free parameter the words then predict the curves of 35 held-out photos from 12 other bodies and +//! 9 other lenses (9.1–105 mm: two compacts, Panasonic, Leica, Olympus, Sigma and Samyang lenses) within 0.12 % of +//! the half diagonal, against up to 13 % uncorrected; corrected renders of all 79 photos line up with the camera +//! JPEG within 0.75 px at 1440 px at every corner measured.] Words 2, 3, 6, 9, 10 +//! and 13 are not used (word 13 is always word 9 + 500; word 10 is fixed per body); words 0, 1, 14 and 15 change +//! from shot to shot. The correction is radial, so it is fitted by the DNG form's odd polynomial `u·(kr0 + kr1 u² + +//! kr2 u⁴ + kr3 u⁶)` over the output (within 0.4 px on a 20 MP sensor); implausible values (a radius far from the +//! active area's, a zoom beyond ±25 %, a correction that folds over) leave the photo uncorrected. +//! //! Not supported ([`RawError::Unsupported`], the embedded preview is shown instead): compression 34826, raw formats //! 1 and 3, and bit depths other than the ones above (no samples of any of them). use super::pef::{Bits, Huffman, diff}; use super::white_from_data; -use crate::{BlackLevel, Cfa, ColorData, MAX_SAMPLES, Mode, OpcodeLists, RawData, RawError, RawFormat, RawImage, Rect, Result}; +use crate::{BlackLevel, Cfa, ColorData, MAX_SAMPLES, Mode, Opcode, OpcodeLists, RawData, RawError, RawFormat, RawImage, Rect, Result}; use lightcraft_geom::Orientation; use lightcraft_tiff::{ByteOrder, Ifd, Tiff, Value, tags as t}; use rayon::prelude::*; @@ -113,6 +139,14 @@ const FORMAT8_FIXED: [(u16, &[u16]); 9] = [ (0x0041, &[17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), (0x0043, &[1]), ]; +/// The camera's lens distortion correction (ExifTool name: DistortionInfo); see the module docs. +const DISTORTION_INFO: u16 = 0x0119; +/// Fixed-point one of the distortion coefficients and zoom. +const DISTORTION_ONE: f64 = 32768.0; +/// Output radii sampled to fit the inverse mapping. +const DISTORTION_SAMPLES: usize = 64; +/// Largest error of the fitted inverse (relative to the half diagonal) accepted: real lenses fit within 1.2e-4. +const DISTORTION_MAX_FIT_ERROR: f64 = 1e-3; /// Most strips seen in a file is 3 (the 12008 × 8008 high-resolution mode). const MAX_STRIPS: usize = 64; @@ -586,13 +620,121 @@ pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result { color: ColorData::default(), wb_multipliers: wb, linearized: false, - opcodes: OpcodeLists::default(), + opcodes: OpcodeLists { list3: distortion(ifd0, active).into_iter().collect(), ..Default::default() }, metadata, }; img.validate_for(mode)?; Ok(img) } +/// The camera's distortion correction (tag `0x0119`, see the module docs) as the equivalent DNG `WarpRectilinear` +/// opcode for `OpcodeList3` (relative to the active area), so it reaches the same "profile corrections" as a DNG's +/// embedded warp. `None` when the camera recorded the correction as off, when it is the identity, or when the +/// values are implausible (the photo is then shown uncorrected rather than wrongly warped). +fn distortion(ifd: &Ifd, active: Rect) -> Option { + let b = ifd.bytes(DISTORTION_INFO)?; + let word = |i: usize| -> Option<[u8; 2]> { b.get(2 * i..2 * i + 2)?.try_into().ok() }; + let signed = |i: usize| word(i).map(|w| i16::from_le_bytes(w) as f64); + if u16::from_le_bytes(word(7)?) & 0x000f != 1 { + return None; + } + let (k2, k4, k6) = (signed(8)? / DISTORTION_ONE, signed(4)? / DISTORTION_ONE, signed(11)? / DISTORTION_ONE); + let zoom = 1.0 + signed(5)? / DISTORTION_ONE; + let radius = u16::from_le_bytes(word(12)?) as f64; + if k2 == 0.0 && k4 == 0.0 && k6 == 0.0 && zoom == 1.0 { + return None; + } + // DNG's normalisation: the distance from the centre to the farthest corner, in pixel-index units + let half_diag = (active.width as f64 - 1.0).hypot(active.height as f64 - 1.0) / 2.0; + if !(half_diag >= 1.0 && (0.5 * half_diag..=2.0 * half_diag).contains(&radius) && (0.75..=1.25).contains(&zoom)) { + return None; + } + // the camera's correction: a source point at radius ρ (in units of `radius`) moves to ρ·g(ρ) + let corrected = |r: f64| { + let r2 = r * r; + r * (1.0 + r2 * (k2 + r2 * (k4 + r2 * k6))) + }; + // the output corners show corrected radius `t_max`; the map must rise monotonically to it (a fold is no lens) + let t_max = half_diag * zoom / radius; + let mut top = None; + let mut prev = 0.0; + for i in 1..=4096 { + let r = 3.0 * t_max * i as f64 / 4096.0; + let v = corrected(r); + if v.is_nan() || v <= prev { + return None; + } + prev = v; + if v >= t_max { + top = Some(r); + break; + } + } + let top = top?; + let source = |t: f64| { + let (mut lo, mut hi) = (0.0, top); + for _ in 0..60 { + let m = 0.5 * (lo + hi); + if corrected(m) < t { lo = m } else { hi = m } + } + 0.5 * (lo + hi) + }; + // least-squares fit of source radius = u·(kr0 + kr1·u² + kr2·u⁴ + kr3·u⁶) over the output radii u ∈ [0, 1] + let samples: Vec<(f64, f64)> = (0..=DISTORTION_SAMPLES) + .map(|i| { + let u = i as f64 / DISTORTION_SAMPLES as f64; + (u, source(u * t_max) * radius / half_diag) + }) + .collect(); + let basis = |u: f64| { + let u2 = u * u; + [u, u * u2, u * u2 * u2, u * u2 * u2 * u2] + }; + let mut a = [[0.0; 4]; 4]; + let mut rhs = [0.0; 4]; + for &(u, v) in &samples { + let f = basis(u); + for (i, fi) in f.iter().enumerate() { + for (j, fj) in f.iter().enumerate() { + a[i][j] += fi * fj; + } + rhs[i] += fi * v; + } + } + let k = solve4(a, rhs)?; + let fit = |u: f64| basis(u).iter().zip(&k).map(|(f, c)| f * c).sum::(); + let worst = samples.iter().map(|&(u, v)| (fit(u) - v).abs()).fold(0.0, f64::max); + if worst.is_nan() || worst > DISTORTION_MAX_FIT_ERROR { + return None; + } + Some(Opcode::WarpRectilinear { planes: vec![[k[0], k[1], k[2], k[3], 0.0, 0.0]], center: [0.5, 0.5] }) +} + +/// Solve the 4 × 4 system `a·x = b` (Gaussian elimination with partial pivoting); `None` when singular or not finite. +fn solve4(mut a: [[f64; 4]; 4], mut b: [f64; 4]) -> Option<[f64; 4]> { + for c in 0..4 { + let p = (c..4).max_by(|&i, &j| a[i][c].abs().total_cmp(&a[j][c].abs()))?; + if a[p][c].is_nan() || a[p][c].abs() <= 1e-300 { + return None; + } + a.swap(c, p); + b.swap(c, p); + for r in c + 1..4 { + let f = a[r][c] / a[c][c]; + for k in c..4 { + a[r][k] -= f * a[c][k]; + } + b[r] -= f * b[c]; + } + } + let mut x = [0.0; 4]; + for c in (0..4).rev() { + let s: f64 = (c + 1..4).map(|k| a[c][k] * x[k]).sum(); + x[c] = (b[c] - s) / a[c][c]; + } + x.iter().all(|v| v.is_finite()).then_some(x) +} + #[cfg(test)] mod tests { use super::*; @@ -853,6 +995,76 @@ mod tests { } } + /// A tag-0x0119 block: 16 little-endian words, the given ones set. + fn distortion_block(words: &[(usize, i16)]) -> Value { + let mut w = [0i16; 16]; + for &(i, v) in words { + w[i] = v; + } + Value::Undefined(w.iter().flat_map(|v| v.to_le_bytes()).collect()) + } + + fn distorted(info: Option) -> Vec { + let (w, h) = (90usize, 60usize); + let px: Vec = (0..w * h).map(|i| (i % 4000) as u16 + 1).collect(); + let mut tags = vec![short(CFA_PATTERN, 1), short(BITS, 12), short(RAW_FORMAT, 5)]; + tags.extend(info.map(|v| (DISTORTION_INFO, v))); + let bytes = file(w as u16, h as u16, &tags, &rotate(pack(&px, 12)), &|off| vec![(RAW_OFFSET, Value::Long(vec![off]))]); + let r = crate::decode(&bytes).unwrap(); + assert_eq!(crate::probe_info(&bytes).unwrap().opcodes, r.opcodes, "headers-only probe reads the same correction"); + r.opcodes.list3 + } + + /// Known answer: correction k2 = 4096/32768 = 1/8 (word 8), zoom 1 − 1024/32768 (word 5), radius 51 px (word 12) + /// on an 86 × 58 active area. A sensor point at ρ appears at ρ + ρ³/8, so the output corner (corrected radius + /// t = half diagonal · zoom / 51) shows the root of ρ³ + 8ρ − 8t = 0, here by Cardano's formula. + #[test] + fn distortion_correction_becomes_a_rectilinear_warp() { + let on = |more: &[(usize, i16)]| { + let mut w = vec![(7, 0x0001), (8, 4096), (5, -1024), (12, 51)]; + w.extend_from_slice(more); + distortion_block(&w) + }; + let ops = distorted(Some(on(&[]))); + let [Opcode::WarpRectilinear { planes, center }] = ops.as_slice() else { panic!("{ops:?}") }; + assert_eq!((planes.len(), *center), (1, [0.5, 0.5])); + let k = planes[0]; + assert_eq!((k[4], k[5]), (0.0, 0.0), "radial only"); + let half_diag = 85f64.hypot(57.0) / 2.0; + for u in [0.1, 0.25, 0.5, 0.75, 1.0] { + let t = u * half_diag * (1.0 - 1024.0 / 32768.0) / 51.0; + let (p, q) = (8.0, -8.0 * t); + let d = (q * q / 4.0 + p * p * p / 27.0).sqrt(); + let rho = (-q / 2.0 + d).cbrt() + (-q / 2.0 - d).cbrt(); + let want = rho * 51.0 / half_diag; + let got = u * (k[0] + k[1] * u * u + k[2] * u.powi(4) + k[3] * u.powi(6)); + // the 4-term odd polynomial approximates the exact inverse (5e-5 of the half diagonal ≈ 0.2 px at 20 MP) + assert!((got - want).abs() < 5e-5, "u {u}: source radius {got}, want {want}"); + } + + // no correction: no block, recorded as off, or the identity + assert!(distorted(None).is_empty()); + assert!(distorted(Some(distortion_block(&[(7, 0x0000), (8, 4096), (12, 51)]))).is_empty(), "off"); + assert!(distorted(Some(distortion_block(&[(7, 0x7f00u16 as i16), (8, 4096), (12, 51)]))).is_empty(), "off, other bits set"); + assert!(distorted(Some(distortion_block(&[(7, 0x0001), (12, 51)]))).is_empty(), "identity"); + // hostile values are ignored, never a panic or a wild warp + for (what, block) in [ + ("truncated", Value::Undefined(vec![1, 0, 2, 0, 3])), + ("radius 0", on(&[(12, 0)])), + ("radius far beyond the sensor", on(&[(12, -1)])), + ("zoom 0", on(&[(5, -32768)])), + ("folds over", on(&[(8, -32768)])), + ("folds over at high order", on(&[(11, -32768), (4, -32768)])), + ("runs away", on(&[(8, 32767), (4, 32767), (11, 32767)])), + ] { + let ops = distorted(Some(block)); + assert!(ops.iter().all(|op| !matches!(op, Opcode::WarpRectilinear { .. })) || what == "runs away", "{what}: {ops:?}"); + if let [Opcode::WarpRectilinear { planes, .. }] = ops.as_slice() { + assert!(planes[0].iter().all(|c| c.is_finite()), "{what}"); + } + } + } + #[test] fn packed_and_word_formats() { let (w, h) = (90usize, 200usize); // > one chunk of data diff --git a/docs/camera-preview-colour.md b/docs/camera-preview-colour.md index 308287c6c..601646426 100644 --- a/docs/camera-preview-colour.md +++ b/docs/camera-preview-colour.md @@ -44,7 +44,7 @@ On one sample, Exposure +1 EV, Saturation +100, Contrast +50, warm/cool WB, tint The same fit, gates and relative WB apply to Panasonic RW2, Leica RWL and the older Panasonic RAW files (all decode as `RawFormat::Rw2`; white balance from tags `0x0024`–`0x0026`). Their embedded JPEG (`JpgFromRaw`, 1920 px wide) always shows the whole active area, while the default crop is the aspect ratio set in the camera (4:3 on the 3:2 sensors, 1:1, 16:9, 2.71 on the S1R II…); when the preview matches the active area rather than the crop, the reference is cropped to the default crop before fitting. -Public CC0 samples from raw.pixls.us (178 files from 118 Panasonic and Leica bodies; the four from 2005–2007 carry no preview), rendered at 1200 px and compared with each file's embedded JPEG (mean CIE76 ΔE at 200 px): 140 of 174 fits accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 (1.2–21.3) with matching mean lightness (median |ΔL*| 0.4); 124 of them use a hue/saturation table. (Fitted with the matrix and tone curve alone, 137 were accepted at median ΔE 6.1; every one of those is as close or closer with the table and chroma curve, by 0.75 on average.) On the 14 corpus files with a preview the neutral fallback scored ΔE 9.6–27 and rendered up to 16 L* too dark; 13 fits were accepted, ΔE 2.8–8.9, lightness within 2 L*. Of the 34 files left on the neutral fallback, 21 fail the gates, almost all compacts and kit zooms (TZ/ZS, FZ2500, D-Lux, V-Lux, GF2, GX80 with its 12–32 mm): their camera JPEG is distortion-corrected, the pixel pairs no longer line up and the held-out RMS stays above 0.10 (GX80: 0.138 → 0.107); two S1 II shots just miss it (0.103, 0.107). The other 13 never reach the gates, mostly nearly colourless scenes (grey walls, concrete: under 5% coloured pixels) or dark ones (a night sky, a near-black frame). Panasonic's embedded distortion data (tag `0x0119`) is not used yet, and `lightcraft-cli calibrate` doesn't collect RW2 files: the misaligned pairs of distortion-corrected JPEGs would distort a pooled profile. +Public CC0 samples from raw.pixls.us (178 files from 118 Panasonic and Leica bodies; the four from 2005–2007 carry no preview), rendered at 1200 px and compared with each file's embedded JPEG (mean CIE76 ΔE at 200 px): 140 of 174 fits accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 (1.2–21.3) with matching mean lightness (median |ΔL*| 0.4); 124 of them use a hue/saturation table. (Fitted with the matrix and tone curve alone, 137 were accepted at median ΔE 6.1; every one of those is as close or closer with the table and chroma curve, by 0.75 on average.) On the 14 corpus files with a preview the neutral fallback scored ΔE 9.6–27 and rendered up to 16 L* too dark; 13 fits were accepted, ΔE 2.8–8.9, lightness within 2 L*. Of the 34 files left on the neutral fallback, 21 fail the gates, almost all compacts and kit zooms (TZ/ZS, FZ2500, D-Lux, V-Lux, GF2, GX80 with its 12–32 mm): their camera JPEG is distortion-corrected, the pixel pairs no longer line up and the held-out RMS stays above 0.10 (GX80: 0.138 → 0.107); two S1 II shots just miss it (0.103, 0.107). The other 13 never reach the gates, mostly nearly colourless scenes (grey walls, concrete: under 5% coloured pixels) or dark ones (a night sky, a near-black frame). Panasonic's embedded distortion data (tag `0x0119`) now corrects the render under Enable Profile Corrections (issue #256) but does not yet align the pairs of this fit, and `lightcraft-cli calibrate` doesn't collect RW2 files: the misaligned pairs of distortion-corrected JPEGs would distort a pooled profile. This is a per-file camera-look estimate, **not measured spectral calibration or Lightroom parity**. Camera local tone, picture styles, noise reduction and lens warps cannot all be reproduced by a global matrix/curve; same-aspect crops may evade the aspect check. Skin, mixed-light scenes and saturated highlights can still differ from a supplied JPEG. Broader Sony and Nikon model/lighting coverage, chart-based calibration, absolute WB and other manufacturers remain unverified. Rejected fits and other RAW formats retain their existing behaviour. diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9c..71177a6e8 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -67,7 +67,8 @@ Take the first one nobody is working on. colour-filter layout, fixed in #85 by reading the file's own tag). 4. **LR-BEHAV-RENDER-FIDELITY** (P1): a side-by-side fidelity suite against Lightroom renders (kept local in `plan/`), then tune tone, highlights, texture/clarity/dehaze, NR and sharpening against it. -5. **LR-EDIT-OPTICS-PROFILE** (P1): a lens-profile database of our own (embedded DNG/maker corrections work today). +5. **LR-EDIT-OPTICS-PROFILE** (P1): a lens-profile database of our own (corrections embedded in DNG files and the + distortion correction Panasonic / Leica cameras record in RW2 / RWL files work today; issue #256). 6. **AI masks and Enhance** (LR-MASK-SUBJECT / SKY / PEOPLE / OBJECTS, LR-EDIT-DETAIL-DENOISE, SUPERRES, LENSBLUR): Object and Describe masks run SAM 3 in pure Rust (`crates/segment`); the weights (SAM License) are never bundled: the app offers a consented, verified download, but **LightCraft's CDN mirrors are not configured yet** (the @@ -254,7 +255,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-EDIT-DETAIL-SUPERRES | Super resolution | P2 | ⬜ | | | | LR-EDIT-DETAIL-AISHARPEN | AI sharpen | OOS | 🚫 | | | | LR-EDIT-OPTICS-CA | Remove chromatic aberration | P1 | ✅ | `crates/ui-egui/src/panels/edit.rs` (checkbox), `ctl:optics.caRed`, `ctl:optics.caBlue` | | -| LR-EDIT-OPTICS-PROFILE | Lens profile corrections | P1 | 🟡 | `ctl:optics.profileDistortion`, `ctl:optics.profileVignetting`, `crates/pipeline/src/optics.rs` | uses corrections embedded in DNG/raw files; no lens-profile database | +| LR-EDIT-OPTICS-PROFILE | Lens profile corrections | P1 | 🟡 | `ctl:optics.profileDistortion`, `ctl:optics.profileVignetting`, `crates/pipeline/src/optics.rs`, `crates/raw/src/vendor/rw2.rs` | uses corrections embedded in the file: DNG `WarpRectilinear` / `FixVignetteRadial`, and the camera's own distortion correction in Panasonic / Leica RW2 / RWL files (tag `0x0119`, read as the equivalent `WarpRectilinear`; on 79 photos from 13 bodies and 11 lenses the corrected render lines up with the camera JPEG within 0.75 px at 1440 px at the corners, from up to 13 % of the half diagonal uncorrected; issue #256); vignetting and lateral CA data of other raw formats (Olympus, Fujifilm, Sony) not read yet; no lens-profile database | | LR-EDIT-OPTICS-DEFRINGE | Defringe | P1 | ✅ | `ctl:optics.defringe*` | no fringe eyedropper | | LR-EDIT-OPTICS-MANUAL | Manual distortion / vignetting | P1 | ✅ | `ctl:optics.distortion`, `ctl:optics.vignetting`, `ctl:optics.vignettingMidpoint` | | | LR-EDIT-GEOM-UPRIGHT | Upright | P1 | ✅ | `cmd:geometry.upright`, `cmd:geometry.guides` | | diff --git a/docs/whats-new.md b/docs/whats-new.md index ea0243f42..2f310b856 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,6 +3,11 @@ ## October 2026 ### RAW decoding +- Panasonic and Leica raws (RW2, RWL) are now corrected for lens distortion the way the camera corrects its own JPEG + (issue #256): the correction the camera records in the file is applied under Lens Corrections ▸ Enable Profile + Corrections, on by default for newly imported photos, with the same framing as the camera's JPEG. At 12 mm the + 12–32 mm kit zoom was off by about 5 % of the image width at the corners before. Files shot with the correction off + are unchanged; photos imported before this change get it when imported again. - Sony ILCE-7M4 downsized lossless ARWs now decode subsampled YCbCr tiles into linear RGB, preserving RAW editing & full-resolution export instead of using embedded JPEG previews. From 15feb6e1fc0f951fa6b5a46feb6db5418f20ab7e Mon Sep 17 00:00:00 2001 From: radialmonster Date: Thu, 8 Oct 2026 00:00:43 -0400 Subject: [PATCH 012/213] LR-PROF-CAMERACOLOR: fit the camera look away from edges when a lens-corrected JPEG misaligns (#232) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sony A7R II camera JPEGs are distortion-corrected by default while the raw is not. On the public CC0 ILCE-7RM2 samples the JPEG is up to 15 px of 1440 off, and the misaligned window frames alone pushed the held-out RMS to 0.108-0.115 (gate 0.10): the photo opened in the grey neutral fallback, 13 L* too dark. The same shot with distortion correction off fits at 0.059. When the fit on all pixels fails the gates, try it once more, with the same gates, on pixels whose 3x3 neighbourhood in either proxy spans a luminance ratio of at most 3. Fits that already pass are unchanged (29 other corpus ARW/NEF/RW2 files render pixel-identical). ILCE-7RM2 ΔE76 vs the camera JPEG (registered) 16.6-16.9 -> 3.7-3.8; corpus GX80 19.1 -> 7.9. RENDER_CACHE_VERSION 11 -> 12. --- crates/engine/src/camera_preview.rs | 119 +++++++++++++++++++++++++--- crates/engine/src/media.rs | 2 +- docs/camera-preview-colour.md | 2 +- docs/parity.md | 2 +- docs/whats-new.md | 2 + 5 files changed, 115 insertions(+), 12 deletions(-) diff --git a/crates/engine/src/camera_preview.rs b/crates/engine/src/camera_preview.rs index cd0d475d2..efadbd98e 100644 --- a/crates/engine/src/camera_preview.rs +++ b/crates/engine/src/camera_preview.rs @@ -102,7 +102,7 @@ pub(crate) fn profile_pairs(raw: &RawImage, bytes: &[u8]) -> Option Option { fit_pairs_with(sensor, reference, None) } +/// Largest luminance ratio within the 3×3 neighbourhood of pixel `i` (1 on flat areas, large on edges). +fn local_contrast(image: &Rgb32f, i: usize) -> f32 { + let (w, h) = (image.width, image.height); + if w == 0 { + return f32::INFINITY; + } + let (x, y) = (i % w, i / w); + let (mut lo, mut hi) = (f32::INFINITY, 0f32); + for row in y.saturating_sub(1)..(y + 2).min(h) { + for col in x.saturating_sub(1)..(x + 2).min(w) { + let l = image.data.get(row * w + col).map_or(0.0, |p| luminance_2020(*p)); + lo = lo.min(l); + hi = hi.max(l); + } + } + if hi.is_finite() { hi / lo.max(1e-4) } else { f32::INFINITY } +} + /// Training pairs (sensor → JPEG, unclipped midtones) and the wider set including highlights -/// (for the chroma curve); `None` when too few, or the photo has too little colour. +/// (for the chroma curve); `None` when too few, or the photo has too little colour. With +/// `edge_limit`, pixels whose 3×3 neighbourhood in either proxy spans a larger luminance ratio +/// are left out: there a small geometric mismatch between the raw and its JPEG pairs unrelated +/// colours (see [`EDGE_CONTRAST`]). type Pairs = Vec<([f64; 3], [f64; 3])>; -fn collect_pairs(sensor: &Rgb32f, reference: &Rgb32f) -> Option<(Pairs, Pairs)> { +fn collect_pairs(sensor: &Rgb32f, reference: &Rgb32f, edge_limit: Option) -> Option<(Pairs, Pairs)> { if (sensor.width, sensor.height) != (reference.width, reference.height) || sensor.data.len() != reference.data.len() { return None; } @@ -161,7 +182,10 @@ fn collect_pairs(sensor: &Rgb32f, reference: &Rgb32f) -> Option<(Pairs, Pairs)> // Highlights too (camera JPEGs bleach colours toward white there), for the chroma curve only. let mut bright = Vec::new(); let mut colour = 0; - for (input, output) in sensor.data.iter().zip(&reference.data) { + for (i, (input, output)) in sensor.data.iter().zip(&reference.data).enumerate() { + if edge_limit.is_some_and(|limit| local_contrast(sensor, i) > limit || local_contrast(reference, i) > limit) { + continue; + } let y = luminance_2020(*output); if !input.iter().all(|v| v.is_finite() && *v > 0.001 && *v < 1.5) || !output.iter().all(|v| v.is_finite() && *v >= 0.0) { continue; @@ -215,11 +239,25 @@ fn fit_matrix(pairs: &[([f64; 3], [f64; 3])]) -> Option { matrix.0.iter().flatten().all(|v| v.is_finite() && v.abs() < 8.0).then_some(matrix) } +/// Neighbourhood luminance ratio above which a pixel counts as an edge for the second attempt +/// of [`fit_pairs_with`]. Camera JPEGs are often lens-corrected (Sony "Distortion Comp.: Auto", +/// compacts and kit zooms): on a 51 mm ILCE-7RM2 shot of a glass façade the JPEG is up to 15 px +/// of 1440 (about one proxy pixel) off the raw, and the fit failed the gate on the mismatched +/// window frames alone (held-out RMS 0.111; 0.059 for the same shot with distortion correction +/// off). Away from edges: 0.049. Issue #232. +const EDGE_CONTRAST: f32 = 3.0; + /// The photo's look: its own matrix (with and without a hue/saturation table), or the given /// colour model (a camera profile's, in the sensor proxy's space), each completed with a tone -/// and chroma curve fitted to this photo. +/// and chroma curve fitted to this photo. When the fit on all pixels fails the acceptance gates, +/// it is tried once more on the pixels away from edges (same gates): there colour pairs stay +/// valid when the camera JPEG's geometry differs slightly from the raw's. fn fit_pairs_with(sensor: &Rgb32f, reference: &Rgb32f, colour: Option<(Mat3, Option)>) -> Option { - let (pairs, bright) = collect_pairs(sensor, reference)?; + fit_pairs_on(sensor, reference, colour.clone(), None).or_else(|| fit_pairs_on(sensor, reference, colour, Some(EDGE_CONTRAST))) +} + +fn fit_pairs_on(sensor: &Rgb32f, reference: &Rgb32f, colour: Option<(Mat3, Option)>, edge_limit: Option) -> Option { + let (pairs, bright) = collect_pairs(sensor, reference, edge_limit)?; let candidates = match colour { Some(given) => vec![given], None => { @@ -270,9 +308,10 @@ fn fit_pairs_with(sensor: &Rgb32f, reference: &Rgb32f, colour: Option<(Mat3, Opt .sum(); if lightcraft_pipeline::profiling() { eprintln!( - "[profile] camera look holdout RMS {:.5} -> {:.5} ({samples} channels)", + "[profile] camera look holdout RMS {:.5} -> {:.5} ({samples} channels{})", (before / samples as f64).sqrt(), - (after / samples as f64).sqrt() + (after / samples as f64).sqrt(), + if edge_limit.is_some() { ", away from edges" } else { "" } ); } if samples == 0 || after >= before * MIN_IMPROVEMENT || after / samples as f64 > MAX_HOLDOUT_RMS.powi(2) { @@ -740,7 +779,7 @@ mod tests { let mut pool = Vec::new(); for seed in 0..4 { let (sensor, reference) = photo(seed, 2.0 + seed as f32 * 0.5); - pool.extend(collect_pairs(&sensor, &reference).unwrap().0); + pool.extend(collect_pairs(&sensor, &reference, None).unwrap().0); } let (matrix, _) = fit_profile(&pool).unwrap(); // the camera's colours (luminance-normalised: the tone curve sets brightness) @@ -818,6 +857,68 @@ mod tests { assert!(info.relative_wb && info.as_shot_temp == 6500.0 && info.as_shot_tint == 0.0); } + /// Issue #232 (skipped without the corpus): the public ILCE-7RM2 sample's camera JPEG is + /// distortion-corrected, and on its glass façade the fit on all pixels misses the gate + /// (held-out RMS 0.113): it used to open grey with the neutral fallback. + #[test] + fn corpus_arw_with_a_lens_corrected_preview_gets_a_camera_look() { + let path = std::env::var_os("LIGHTCRAFT_CORPUS") + .map(std::path::PathBuf::from) + .unwrap_or_else(|| std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus")) + .join("raw/arw-sony-a7rm2-12bit-uncompressed.arw"); + let Ok(bytes) = std::fs::read(&path) else { + eprintln!("skip: {} absent", path.display()); + return; + }; + let (_, info) = crate::files::load_bytes(&bytes, 400).unwrap(); + assert!(info.camera_tone.is_some(), "no camera look fitted"); + } + + /// Issue #232: an ILCE-7RM2 camera JPEG is lens-corrected ("Distortion Comp.: Auto"), so on a + /// façade of window frames the raw and its JPEG are a pixel or two apart. The colours agree + /// everywhere away from the edges, and the look must still be found there. + #[test] + fn fits_a_slightly_misaligned_camera_jpeg_away_from_edges() { + let known = Mat3([[1.8, -0.4, -0.1], [-0.2, 1.5, -0.1], [-0.05, -0.3, 1.7]]); + let (w, h, block, shift) = (96, 64, 8, 2); + // Patches of 8×8 pixels, alternately dark and bright (frames and panes), varied in colour. + let scene = |x: usize, y: usize| -> [f32; 3] { + let (bx, by) = (x / block, y / block); + let ev = if (bx + by) % 2 == 0 { 0.04 + (bx % 3) as f32 * 0.01 } else { 0.3 + (by % 4) as f32 * 0.08 }; + [ev * (0.7 + ((bx * 7 + by * 3) % 11) as f32 * 0.05), ev, ev * (0.7 + ((bx * 5 + by) % 13) as f32 * 0.04)] + }; + let camera = |p: [f32; 3]| { + let q = known.apply_f32(p); + let y = luminance_2020(q); + q.map(|v| v * (1.0 - (-2.5 * y).exp()) / y) + }; + let mut sensor = Rgb32f::new(w, h); + let mut reference = sensor.clone(); + for y in 0..h { + for x in 0..w { + sensor.data[y * w + x] = scene(x, y); + // The JPEG shows the scene `shift` pixels to the right of where the raw has it. + reference.data[y * w + x] = camera(scene(x.saturating_sub(shift), y)); + } + } + let fit = fit_pairs(&sensor, &reference).expect("look fitted away from the misaligned edges"); + // The look itself is right: on the aligned scene it reproduces the camera. + let tone = ToneMap::camera(&fit.tone, 0.0, 0.0, 0.0); + let (mut error, mut n) = (0.0, 0); + for y in 0..h { + for x in 0..w { + let p = displayed(fit.matrix.apply(scene(x, y).map(f64::from)), &tone); + let t = camera(scene(x, y)); + error += (0..3).map(|c| (p[c] - f64::from(t[c])).powi(2)).sum::(); + n += 3; + } + } + assert!((error / n as f64).sqrt() < 0.03, "{}", (error / n as f64).sqrt()); + // Unrelated colours stay rejected away from edges too (covered below); a flat JPEG as well. + reference.data.fill([0.2; 3]); + assert!(fit_pairs(&sensor, &reference).is_none()); + } + #[test] fn rejects_monochrome_invalid_and_unrelated_previews() { let mut sensor = Rgb32f::new(32, 32); diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a2..7aafbf349 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; diff --git a/docs/camera-preview-colour.md b/docs/camera-preview-colour.md index 308287c6c..483714808 100644 --- a/docs/camera-preview-colour.md +++ b/docs/camera-preview-colour.md @@ -14,7 +14,7 @@ Matrix-only and matrix + table looks are each completed (tone curve, then chroma The matrix is applied to scene-linear sensor pixels. The curve is carried in `SourceInfo` and applied in the pipeline finish stage, after exposure/WB/local edits. It is never baked into decoded luminance or clipped to JPEG precision. CPU and GPU use the same tone LUT. Contrast, whites and blacks adjust the starting look through the display-domain controls. JPEG neutral rendering and the existing DNG colour model are unchanged. -One third of the pixels are held out from both fits. The combined model must reduce linear-display squared error by at least 30% and have per-channel RMS error ≤0.10 (0.055 rejected half of the public raw.pixls.us samples tried — ILCE-6400, -6700, -7M4 — whose fits were still 1.5–3× closer to the camera JPEG than the fallback and visibly better). These are provisional acceptance gates, not a colour-accuracy certification. Monochrome, singular, unrelated, aspect-mismatched and poor-fitting references retain the documented fallback. Both fits use the same fixed proxy at every decode resolution. +One third of the pixels are held out from both fits. The combined model must reduce linear-display squared error by at least 30% and have per-channel RMS error ≤0.10 (0.055 rejected half of the public raw.pixls.us samples tried — ILCE-6400, -6700, -7M4 — whose fits were still 1.5–3× closer to the camera JPEG than the fallback and visibly better). When the fit on all pixels fails these gates, it is tried once more, with the same gates, on the pixels away from edges (where the 3×3 neighbourhood in either proxy spans a luminance ratio above 3). Many camera JPEGs are lens-corrected while the raw is not (Sony "Distortion Comp.: Auto", compacts and kit zooms), and on a detailed scene the misaligned edges alone fail the gate although the colours agree everywhere else (issue #232). The public ILCE-7RM2 samples (raw.pixls.us, one façade shot in seven compression/correction variants, 24–70 mm at 51 mm) are up to 15 px of 1440 off their JPEG; the six with distortion correction on missed the gate at held-out RMS 0.108–0.115 and opened in the grey neutral fallback, 13 L* too dark (ΔE76 16.6–16.9 against the JPEG after registering its geometry). Away from edges they pass at 0.049–0.052 and land at ΔE 3.7–3.8 with lightness within 0.6 L*, like the variant shot with distortion correction off (0.059 on all pixels, ΔE 3.9). The corpus GX80 kit-zoom sample (up to 63 px of 1440 off) passes too: 0.107 → 0.081, ΔE 19.1 → 7.9. Fits accepted on all pixels are unchanged. These are provisional acceptance gates, not a colour-accuracy certification. Monochrome, singular, unrelated, aspect-mismatched and poor-fitting references retain the documented fallback. Both fits use the same fixed proxy at every decode resolution. ## Source metadata and white balance diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9c..edefc83f3 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -276,7 +276,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-PROF-ADOBE | Standard raw looks (own equivalents) | P0 | ✅ | `crates/engine/src/presets.rs` (`PROFILES`), `crates/pipeline/src/profiles.rs` | six own looks: Color, Neutral, Vivid, Landscape, Portrait, Monochrome | | LR-PROF-ADAPTIVE | Adaptive profiles | P2 | ⬜ | | | | LR-PROF-CAMERA | Camera-matching looks | P2 | ⬜ | | | -| LR-PROF-CAMERACOLOR | Camera colour calibration (own) | P0 | 🟡 | `crates/raw/src/color.rs`, `crates/raw/src/profile.rs`, `crates/engine/src/camera_preview.rs` | DNG files use the colour matrices and the camera-profile look they carry (`ProfileHueSatMap`, `ProfileLookTable`, `ProfileToneCurve`, read from the file at run time per DNG spec ch. 6 — Lightroom-converted DNGs rendered flat and muted without them, issue #138); Sony ARW, Nikon NEF and Panasonic RW2 get a guarded file-local fit to their own embedded JPEG — chromaticity matrix + hue/saturation/value table + tone and chroma curves, relative WB — or, with a local per-model profile pooled from many photos (`lightcraft-cli calibrate`, ARW and NEF so far, `crates/engine/src/camera_profiles.rs`), the profile's colour and only their own tone/chroma curves (on 29 held-out ILCE-7M4 photos mean ΔE vs the camera JPEG 3.54 → 3.10), kept with smart previews (see docs/camera-preview-colour.md); ARW: on 10 public raw.pixls.us samples from 8 bodies 7 fits were accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples (D750, D780, D850, D7500, Z 50 and local D7500 shots) accepted, ΔE 13–46 → 3–8 (issue #150); RW2 / RWL: 140 of 174 public samples accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 with matching lightness (13 of the 14 corpus files with a preview: ΔE 9.6–27 → 2.8–8.9; most rejections are compacts and kit zooms whose camera JPEG is distortion-corrected); this is not measured camera calibration; other raws and rejected fits use a neutral fallback (camera RGB ≈ linear sRGB, `matrix_is_fallback`) with as-shot white balance, so colours are muted and not accurate. Needs our own per-camera calibration: matrices the files carry themselves (e.g. Olympus `ColorMatrix`), fitting each camera to its own embedded JPEG, then chart shots. Adobe matrices / DCPs are never used. Biggest image-quality gap today | +| LR-PROF-CAMERACOLOR | Camera colour calibration (own) | P0 | 🟡 | `crates/raw/src/color.rs`, `crates/raw/src/profile.rs`, `crates/engine/src/camera_preview.rs` | DNG files use the colour matrices and the camera-profile look they carry (`ProfileHueSatMap`, `ProfileLookTable`, `ProfileToneCurve`, read from the file at run time per DNG spec ch. 6 — Lightroom-converted DNGs rendered flat and muted without them, issue #138); Sony ARW, Nikon NEF and Panasonic RW2 get a guarded file-local fit to their own embedded JPEG — chromaticity matrix + hue/saturation/value table + tone and chroma curves, relative WB — or, with a local per-model profile pooled from many photos (`lightcraft-cli calibrate`, ARW and NEF so far, `crates/engine/src/camera_profiles.rs`), the profile's colour and only their own tone/chroma curves (on 29 held-out ILCE-7M4 photos mean ΔE vs the camera JPEG 3.54 → 3.10), kept with smart previews (see docs/camera-preview-colour.md); ARW: on 10 public raw.pixls.us samples from 8 bodies 7 fits were accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples (D750, D780, D850, D7500, Z 50 and local D7500 shots) accepted, ΔE 13–46 → 3–8 (issue #150); RW2 / RWL: 140 of 174 public samples accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 with matching lightness (13 of the 14 corpus files with a preview: ΔE 9.6–27 → 2.8–8.9; most rejections are compacts and kit zooms whose camera JPEG is distortion-corrected); a fit that fails the gates on all pixels is retried away from edges, where a lens-corrected camera JPEG still lines up (issue #232: the six distortion-corrected public ILCE-7RM2 samples ΔE 16.6–16.9 → 3.7–3.8, the corpus GX80 19.1 → 7.9); this is not measured camera calibration; other raws and rejected fits use a neutral fallback (camera RGB ≈ linear sRGB, `matrix_is_fallback`) with as-shot white balance, so colours are muted and not accurate. Needs our own per-camera calibration: matrices the files carry themselves (e.g. Olympus `ColorMatrix`), fitting each camera to its own embedded JPEG, then chart shots. Adobe matrices / DCPs are never used. Biggest image-quality gap today | | LR-PROF-CREATIVE | Creative profiles (own) | P2 | ✅ | `cmd:develop.profile`, `crates/pipeline/src/profiles.rs` | 16 own looks in Film / Cinematic / Muted / B&W (tone + point-curve fades, colour grading, mixer / B&W mix); scale with `ctl:profile.amount`; sliders untouched | | LR-PROF-LEGACY | Legacy profiles | P2 | ⬜ | | | | LR-PROF-NONRAW | Profiles for non-raw files | P0 | ✅ | `cmd:develop.profile` | same looks apply to JPEG/TIFF | diff --git a/docs/whats-new.md b/docs/whats-new.md index ea0243f42..041238827 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,6 +3,8 @@ ## October 2026 ### RAW decoding +- Sony A7R II (and other) raws whose camera JPEG is lens-corrected no longer open grey and too dark (issue #232): the + starting look is fitted to the camera JPEG away from edges when the misaligned edges spoil the fit on all pixels. - Sony ILCE-7M4 downsized lossless ARWs now decode subsampled YCbCr tiles into linear RGB, preserving RAW editing & full-resolution export instead of using embedded JPEG previews. From 841850d5162b0120284b8c98848fbab5885a49ba Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:14:58 +0400 Subject: [PATCH 013/213] Delete crates/ui-egui/locales/ru.json --- crates/ui-egui/locales/ru.json | 1263 -------------------------------- 1 file changed, 1263 deletions(-) delete mode 100644 crates/ui-egui/locales/ru.json diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json deleted file mode 100644 index b935e1910..000000000 --- a/crates/ui-egui/locales/ru.json +++ /dev/null @@ -1,1263 +0,0 @@ -{ - "+ Group": "+ Группа", - "+ Rule": "+ Условие", - "0: nothing is rejected.": "0: ничего не отклоняется.", - "A filter is hiding this view's photos: change it, or clear it (View → Clear Filters)": "Фильтр скрывает фото в этом представлении: измените его или сбросьте (Вид → Сбросить фильтры)", - "A one-time download of about": "Однократная загрузка около", - "About LightCraft": "О LightCraft", - "Activity": "Активность", - "Actual size": "Реальный размер", - "Add": "Добавить", - "Add Catchlight": "Добавить блик в глазах", - "Add Folder to Local": "Добавить папку в «Локальное»", - "Add Point Color Sample": "Добавить точечный образец цвета", - "Add Red Eye Correction": "Добавить коррекцию красных глаз", - "Add Remove Spot": "Добавить удаление пятна", - "Add Selected Photos": "Добавить выбранные фото", - "Add a rule (⌥-click: a group)": "Добавить условие (⌥+щелчок: группу)", - "Add in place": "Добавить на месте", - "Add in place: the library references the files where they are; nothing is copied or moved.": "Добавить на месте: библиотека ссылается на файлы там, где они лежат; ничего не копируется и не перемещается.", - "Add keyword": "Добавить ключевое слово", - "Add number": "Добавить номер", - "Add to": "Добавить в", - "Add to Album": "Добавить в альбом", - "Add to Favorites": "Добавить в избранное", - "Add to My Photos": "Добавить в «Мои фото»", - "Add to Selected Photos": "Добавить к выбранным фото", - "Add to Target Album": "Добавить в целевой альбом", - "Add to this mask": "Добавить к этой маске", - "Add/Subtract/Intersect Mask": "Добавить/вычесть/пересечь маску", - "Added to photos you import that don't already have it.": "Добавляется к импортируемым фото, у которых этого ещё нет.", - "Adding": "Добавление", - "Advanced search…": "Расширенный поиск…", - "After": "После", - "After 30 days": "Через 30 дней", - "After 7 days": "Через 7 дней", - "After 90 days": "Через 90 дней", - "After a year": "Через год", - "Album": "Альбом", - "Albums": "Альбомы", - "All": "Все", - "All Metadata": "Все метаданные", - "All Metadata…": "Все метаданные…", - "All Photos": "Все фото", - "All rights reserved": "Все права защищены", - "All types": "Все типы", - "Alt Text": "Альтернативный текст", - "Always": "Всегда", - "Amount": "Количество", - "Aperture": "Диафрагма", - "Application": "Приложение", - "Apply Color Label Set": "Применить набор цветовых меток", - "Apply Filter Preset": "Применить пресет фильтра", - "Apply Metadata Preset": "Применить пресет метаданных", - "Apply Point Curve Preset": "Применить пресет точечной кривой", - "Apply Preset": "Применить пресет", - "Apply Settings JSON": "Применить JSON настроек", - "Approved": "Одобрено", - "Aqua": "Голубой", - "Aqua Hue": "Тон голубого", - "Aqua Luminance": "Яркость голубого", - "Aqua Saturation": "Насыщенность голубого", - "ArtCraft Website": "Сайт ArtCraft", - "ArtCraft — more creative apps": "ArtCraft — другие творческие приложения", - "As Shot": "Как снято", - "As shot": "Как снято", - "Ascending": "По возрастанию", - "Aspect": "Пропорции", - "Aspect Ratio": "Соотношение сторон", - "Assisted Culling": "Автоматический отбор", - "Assisted Culling…": "Автоматический отбор…", - "Auto": "Авто", - "Auto Advance": "Автопереход", - "Auto Advance Off": "Автопереход выкл.", - "Auto Advance On": "Автопереход вкл.", - "Auto Advance after rating or flagging": "Автопереход после оценки или пометки флагом", - "Auto Advance: move to the next photo after rating or flagging": "Автопереход: переходить к следующему фото после оценки или пометки флагом", - "Auto Align": "Автовыравнивание", - "Auto B&W Mix": "Авто-микс Ч/Б", - "Auto Crop": "Автокадрирование", - "Auto Import": "Автоимпорт", - "Auto Import Now": "Выполнить автоимпорт сейчас", - "Auto Import Settings": "Настройки автоимпорта", - "Auto Mask": "Автомаска", - "Auto Settings": "Автонастройки", - "Auto Straighten": "Автовыравнивание горизонта", - "Auto Sync": "Автосинхронизация", - "Auto-Stack by Capture Time": "Автостопка по времени съёмки", - "Auto-Stack by Capture Time…": "Автостопка по времени съёмки…", - "Auto-Tag Photos from Tracklog": "Автопривязка фото по треку", - "Auto-Tag from Tracklog": "Автопривязка по треку", - "Auto-Tag from Tracklog…": "Автопривязка по треку…", - "Automatic": "Автоматически", - "Automatically Write Changes into XMP": "Автоматически записывать изменения в XMP", - "Automatically write changes into XMP sidecars": "Автоматически записывать изменения в XMP-файлы", - "Autumn Gold": "Осеннее золото", - "B&W": "Ч/Б", - "B&W Mix": "Микс Ч/Б", - "B&W Mixer": "Микшер Ч/Б", - "Back": "Назад", - "Back Up Library…": "Резервная копия библиотеки…", - "Back to Edit": "Вернуться к редактированию", - "Back to Edit (E)": "Вернуться к редактированию (E)", - "Back to Grid": "Вернуться к сетке", - "Background": "Фон", - "Balance": "Баланс", - "Basic": "Основные", - "Before": "До", - "Before/After (Y)": "До/после (Y)", - "Before/After Settings": "Настройки «до/после»", - "Before/After Split": "Разделение «до/после»", - "Before/After Split Top/Bottom": "Разделение «до/после» сверху/снизу", - "Before/After Top/Bottom": "До/после сверху/снизу", - "Begin Interaction": "Начать взаимодействие", - "Bit depth": "Разрядность", - "Black & White": "Чёрно-белое", - "Blacks": "Чёрные", - "Bleach Bypass": "Обход отбеливания", - "Bleached": "Выбеленный", - "Blending": "Наложение", - "Blue": "Синий", - "Blue Hour Boost": "Усиление синего часа", - "Blue Hue": "Тон синего", - "Blue Luminance": "Яркость синего", - "Blue Primary": "Основной синий", - "Blue Saturation": "Насыщенность синего", - "Blue channel": "Синий канал", - "Blue/Yellow Fringe": "Сине-жёлтая кайма", - "Blurry photos below the score are flagged as rejects.": "Размытые фото с оценкой ниже порога помечаются как отклонённые.", - "Boundary Warp": "Деформация границ", - "Bright & Airy": "Светлый и воздушный", - "Bring All to Front": "Показать все окна", - "Browse Folder": "Выбрать папку", - "Browse Folder…": "Выбрать папку…", - "Browse Profiles": "Обзор профилей", - "Browse…": "Обзор…", - "Brush": "Кисть", - "Brush Settings": "Настройки кисти", - "Brush Stroke": "Мазок кисти", - "Build Previews": "Создать просмотры", - "Build Smart Previews": "Создать смарт-просмотры", - "By Date": "По дате", - "By day (YYYY/YYYY-MM-DD)": "По дням (ГГГГ/ГГГГ-ММ-ДД)", - "By month (YYYY/YYYY-MM)": "По месяцам (ГГГГ/ГГГГ-ММ)", - "Calibration": "Калибровка", - "Camera": "Камера", - "Cameras and Cards": "Камеры и карты", - "Cancel": "Отмена", - "Cancel Download": "Отменить загрузку", - "Cancel Interaction": "Отменить взаимодействие", - "Cancel Preview Build": "Отменить создание просмотров", - "Candidate": "Кандидат", - "Caption": "Подпись", - "Capture Date": "Дата съёмки", - "Captured": "Снято", - "Catalog Statistics": "Статистика каталога", - "Check All": "Отметить все", - "Check Output Path": "Проверить путь вывода", - "Choose Another Library…": "Выбрать другую библиотеку…", - "Choose Edit Settings to Copy": "Выбрать настройки для копирования", - "Choose Edit Settings to Copy…": "Выбрать настройки для копирования…", - "Choose Folder…": "Выбрать папку…", - "Choose a photo in the grid or filmstrip": "Выберите фото в сетке или на ленте", - "Choose…": "Выбрать…", - "Cinematic": "Кинематографичный", - "City": "Город", - "Clarity": "Чёткость", - "Clean Studio": "Чистая студия", - "Clear": "Очистить", - "Clear Cache": "Очистить кэш", - "Clear Filters": "Сбросить фильтры", - "Clear Guides": "Убрать направляющие", - "Clear History": "Очистить историю", - "Clear Preview Cache": "Очистить кэш просмотров", - "Clear Quick Collection": "Очистить быструю коллекцию", - "Clear Reference": "Убрать эталон", - "Clear all": "Очистить всё", - "Clear form": "Очистить форму", - "Cleared History": "История очищена", - "Click Zoom Ratio": "Масштаб по щелчку", - "Click a tag to insert it. Examples are for IMG_0042.CR3 taken 14 Jan 2026, 05:58:48.": "Щёлкните по тегу, чтобы вставить его. Примеры приведены для IMG_0042.CR3, снятого 14 января 2026 в 05:58:48.", - "Click photos in the grid to toggle the keyword; Esc stops": "Щёлкайте по фото в сетке, чтобы переключать ключевое слово; Esc — остановить", - "Click the photo to pick a colour · ⇧-click adds more": "Щёлкните по фото, чтобы выбрать цвет · ⇧+щелчок добавляет ещё", - "Click the photo to pick a colour; ⇧-click adds more (up to 5)": "Щёлкните по фото, чтобы выбрать цвет; ⇧+щелчок добавляет ещё (до 5)", - "Clone": "Клонировать", - "Close": "Закрыть", - "Close Panel": "Закрыть панель", - "Close Window": "Закрыть окно", - "Close all": "Закрыть все", - "Close file": "Закрыть файл", - "Cloudy": "Облачно", - "Collapse All Stacks": "Свернуть все стопки", - "Color": "Цвет", - "Color Grading": "Цветокоррекция", - "Color Label Names": "Названия цветовых меток", - "Color Label Sets": "Наборы цветовых меток", - "Color Mixer": "Цветовой микшер", - "Color Noise Reduction": "Подавление цветового шума", - "Color Priority": "Приоритет цвета", - "Color Range": "Цветовой диапазон", - "Color space": "Цветовое пространство", - "Color space: sRGB (AVIF)": "Цветовое пространство: sRGB (AVIF)", - "Combine files…": "Объединить файлы…", - "Command Journal": "Журнал команд", - "Comments panel": "Панель комментариев", - "Compare": "Сравнение", - "Compare (Shift+C)": "Сравнение (Shift+C)", - "Compare Before and After": "Сравнить до и после", - "Compare needs at least two photos": "Для сравнения нужно минимум два фото", - "Component options": "Параметры компонента", - "Compression": "Сжатие", - "Concrete Cool": "Холодный бетон", - "Confirm before deleting": "Подтверждать удаление", - "Confirm before moving photos to Recently Deleted": "Подтверждать перемещение фото в «Недавно удалённые»", - "Constrain Crop": "Ограничить кадрирование", - "Continue Without Saving": "Продолжить без сохранения", - "Contrast": "Контрастность", - "Convert to DNG": "Преобразовать в DNG", - "Cool Fade": "Холодное выцветание", - "Cool Morning": "Холодное утро", - "Copy": "Копировать", - "Copy After's Settings to Before": "Скопировать настройки «после» в «до»", - "Copy Before's Settings to After": "Скопировать настройки «до» в «после»", - "Copy Edit Settings": "Копировать настройки правки", - "Copy History Step Settings to Before": "Скопировать настройки шага истории в «до»", - "Copy Metadata": "Копировать метаданные", - "Copy as DNG": "Копировать как DNG", - "Copy into the library (else use the files where they are)": "Копировать в библиотеку (иначе файлы используются там, где лежат)", - "Copy pages": "Копировать страницы", - "Copy the files (into the library's Originals/, or a folder you choose)": "Копировать файлы (в папку «Originals/» библиотеки или в выбранную папку)", - "Copy to": "Копировать в", - "Copy to Clipboard": "Копировать в буфер обмена", - "Copy: the files are copied to the folder below and the library uses the copies; the originals are left as they are.": "Копирование: файлы копируются в указанную ниже папку, а библиотека использует копии; оригиналы остаются как есть.", - "Copyright": "Авторские права", - "Copyright Info URL": "URL сведений об авторских правах", - "Copyright Status": "Статус авторских прав", - "Country": "Страна", - "Create Album": "Создать альбом", - "Create Album…": "Создать альбом…", - "Create Folder": "Создать папку", - "Create Folder…": "Создать папку…", - "Create New Mask": "Создать новую маску", - "Create PDF from clipboard": "Создать PDF из буфера обмена", - "Create PDF from file…": "Создать PDF из файла…", - "Create PDF from images…": "Создать PDF из изображений…", - "Create Preset": "Создать пресет", - "Create Preset…": "Создать пресет…", - "Create Proof Copy": "Создать пробную копию", - "Create Smart Album": "Создать смарт-альбом", - "Create Smart Album from Filter…": "Создать смарт-альбом из фильтра…", - "Create Smart Album…": "Создать смарт-альбом…", - "Create Stack": "Создать стопку", - "Create Version": "Создать версию", - "Create Version from Step": "Создать версию из шага", - "Create Virtual Copy": "Создать виртуальную копию", - "Creator": "Автор", - "Crisp Landscape": "Чёткий пейзаж", - "Crisp Vista": "Чёткая панорама", - "Crop": "Кадрирование", - "Crop & Rotate": "Кадрирование и поворот", - "Crop & Rotate (C)": "Кадрирование и поворот (C)", - "Crop Aspect": "Пропорции кадра", - "Cross Process": "Кросс-процесс", - "Culling": "Отбор", - "Curve": "Кривая", - "Custom": "Свои", - "Custom…": "Свои…", - "Cut": "Вырезать", - "Cut pages": "Вырезать страницы", - "Cycle Crop Overlay": "Переключить сетку кадрирования", - "Cycle Crop Overlay Orientation": "Переключить ориентацию сетки кадрирования", - "Cycle Info Overlay": "Переключить информационную накладку", - "Cycle Mask Overlay Color": "Переключить цвет наложения маски", - "Cycle Mask Overlay Mode": "Переключить режим наложения маски", - "Cylindrical": "Цилиндрическая", - "Dark & Moody": "Тёмный и атмосферный", - "Darken": "Затемнить", - "Darks": "Тёмные тона", - "Date Groups": "Группы по датам", - "Day": "День", - "Daylight": "Дневной свет", - "Decrease Brush Feather": "Уменьшить растушёвку кисти", - "Decrease Brush Size": "Уменьшить размер кисти", - "Deep Sky": "Глубокое небо", - "Defringe": "Убрать кайму", - "Deghost Amount": "Степень удаления артефактов", - "Dehaze": "Убрать дымку", - "Delete": "Удалить", - "Delete (⌫)": "Удалить (⌫)", - "Delete Album": "Удалить альбом", - "Delete All Masks": "Удалить все маски", - "Delete Color Label Set": "Удалить набор цветовых меток", - "Delete Export Preset": "Удалить пресет экспорта", - "Delete Filter Preset": "Удалить пресет фильтра", - "Delete Imported Profile": "Удалить импортированный профиль", - "Delete Keyword": "Удалить ключевое слово", - "Delete Keyword Set": "Удалить набор ключевых слов", - "Delete Mask": "Удалить маску", - "Delete Metadata Preset": "Удалить пресет метаданных", - "Delete Permanently": "Удалить навсегда", - "Delete Photo": "Удалить фото", - "Delete Photos": "Удалить фото", - "Delete Point Color Sample": "Удалить точечный образец цвета", - "Delete Point Curve Preset": "Удалить пресет точечной кривой", - "Delete Preset": "Удалить пресет", - "Delete Red Eye Correction": "Удалить коррекцию красных глаз", - "Delete Spot": "Удалить пятно", - "Delete Version": "Удалить версию", - "Delete all": "Удалить все", - "Delete all spots": "Удалить все пятна", - "Delete mask": "Удалить маску", - "Delete them": "Удалить их", - "Density": "Плотность", - "Depth Range": "Диапазон глубины", - "Descending": "По убыванию", - "Describe…": "Описать…", - "Deselect": "Снять выделение", - "Deselect All": "Снять выделение со всех", - "Desert Heat": "Жар пустыни", - "Desert Warmth": "Тепло пустыни", - "Detail": "Детализация", - "Detail (D)": "Детализация (D)", - "Details": "Подробности", - "Diagonal": "По диагонали", - "Discard Preview Cache": "Удалить кэш просмотров", - "Display P3": "Display P3", - "Display theme": "Тема оформления", - "Distortion": "Искажение", - "Document properties…": "Свойства документа…", - "Don't enlarge": "Не увеличивать", - "Done": "Готово", - "Drag along the horizon; double-click for Auto": "Ведите вдоль горизонта; двойной щелчок — авто", - "Drag over an eye on the photo; the pupil inside is found automatically.": "Наведите на глаз на фото; зрачок внутри будет найден автоматически.", - "Draw Guides": "Нарисовать направляющие", - "Duplicate": "Дублировать", - "Duplicate Mask": "Дублировать маску", - "Duplicate and Invert Mask": "Дублировать и инвертировать маску", - "Each / starts a folder level; tags are filled in per photo (a level with missing metadata is \"unknown\").": "Каждый символ / начинает новый уровень папок; теги подставляются для каждого фото (уровень без метаданных называется «unknown»).", - "Edit": "Правка", - "Edit (E)": "Правка (E)", - "Edit Capture Time": "Изменить время съёмки", - "Edit Capture Time…": "Изменить время съёмки…", - "Edit Color Label Names": "Изменить названия цветовых меток", - "Edit Color Label Names…": "Изменить названия цветовых меток…", - "Edit Copy for External Editor": "Править копию во внешнем редакторе", - "Edit Info": "Править сведения", - "Edit Label Names…": "Изменить названия меток…", - "Edit Mask Component": "Изменить компонент маски", - "Edit Sections": "Изменить разделы", - "Edit Smart Album": "Изменить смарт-альбом", - "Edit Smart Album…": "Изменить смарт-альбом…", - "Edit Spot": "Изменить пятно", - "Edit in External Editor": "Править во внешнем редакторе", - "Edited": "Изменено", - "Effect": "Эффект", - "Effects": "Эффекты", - "Enable Lens Corrections": "Включить коррекцию объектива", - "End Interaction": "Завершить взаимодействие", - "English": "Английский", - "Enter Full Screen": "Полноэкранный режим", - "Erase": "Стереть", - "Every EXIF, GPS and XMP field in the file": "Все поля EXIF, GPS и XMP в файле", - "Existing previews": "Имеющиеся просмотры", - "Exit": "Выход", - "Expand All Stacks": "Развернуть все стопки", - "Expand/Collapse Stack": "Развернуть/свернуть стопку", - "Export": "Экспорт", - "Export Album with Preset": "Экспортировать альбом с пресетом", - "Export Album…": "Экспортировать альбом…", - "Export As…": "Экспортировать как…", - "Export Group…": "Экспортировать группу…", - "Export Now": "Экспортировать сейчас", - "Export Point Curve Presets": "Экспортировать пресеты точечной кривой", - "Export Point Curve Presets…": "Экспортировать пресеты точечной кривой…", - "Export Presets": "Экспортировать пресеты", - "Export Presets…": "Экспортировать пресеты…", - "Export User Presets…": "Экспортировать пользовательские пресеты…", - "Export with Preset": "Экспорт с пресетом", - "Export with Previous": "Экспорт с предыдущими настройками", - "Exporting": "Экспорт", - "Export…": "Экспорт…", - "Exposure": "Экспозиция", - "Exposure (shutter · aperture · ISO)": "Экспозиция (выдержка · диафрагма · ISO)", - "Extended Description": "Расширенное описание", - "External Editor": "Внешний редактор", - "Faded Matte": "Выцветший матовый", - "Faded Slide": "Выцветший слайд", - "Faded Urban": "Выцветший урбан", - "Favorite Preset": "Избранный пресет", - "Favorite Profile": "Избранный профиль", - "Favorites": "Избранное", - "Feather": "Растушёвка", - "File": "Файл", - "File & date": "Файл и дата", - "File Name": "Имя файла", - "File Path": "Путь к файлу", - "File Size": "Размер файла", - "File name": "Имя файла", - "Fill Edges": "Заполнить края", - "Film": "Плёнка", - "Filmstrip": "Лента", - "Filmstrip (/)": "Лента (/)", - "Filter": "Фильтр", - "Filter Bar": "Панель фильтров", - "Filter Presets": "Пресеты фильтров", - "Filter bar": "Панель фильтров", - "Filter fields": "Поля фильтра", - "Find Dust Spots": "Найти пылинки", - "Find Missing Photos": "Найти отсутствующие фото", - "Find Missing Photos…": "Найти отсутствующие фото…", - "Find Similar Photos": "Найти похожие фото", - "Find tools and commands…": "Найти инструменты и команды…", - "Find…": "Найти…", - "Fit": "Вписать", - "Fit (F)": "Вписать (F)", - "Fit on Screen": "Вписать в экран", - "Fit to width": "По ширине", - "Fit visible": "Вписать видимое", - "Flag": "Флаг", - "Flag as Pick": "Пометить как выбранное", - "Flag as Reject": "Пометить как отклонённое", - "Flash": "Вспышка", - "Flip H": "Отразить по гор.", - "Flip Horizontal": "Отразить по горизонтали", - "Flip Overlay (⇧O)": "Отразить наложение (⇧O)", - "Flip V": "Отразить по верт.", - "Flip Vertical": "Отразить по вертикали", - "Flow": "Поток", - "Fluorescent": "Флуоресцентная лампа", - "Focal length": "Фокусное расстояние", - "Folder": "Папка", - "Folder name (renamed on disk; its photos follow)": "Имя папки (переименовывается на диске; фото следуют за ней)", - "Folders": "Папки", - "Folders and {date} use the capture time; a photo without one uses today's date.": "Папки и {date} используют время съёмки; фото без него получает сегодняшнюю дату.", - "Food": "Еда", - "Forget Unchanged Local Photos": "Забыть неизменённые локальные фото", - "Forget unchanged photos": "Забыть неизменённые фото", - "Form fields panel": "Панель полей формы", - "Format": "Формат", - "Forward": "Вперёд", - "Free": "Свободно", - "Fresh & Bright": "Свежий и яркий", - "Friday": "Пятница", - "Full": "Полный", - "Full Screen Preview": "Полноэкранный просмотр", - "Full screen mode": "Полноэкранный режим", - "Full size": "Полный размер", - "GPS Track Log": "GPS-трек", - "GPU Rendering": "Рендеринг на GPU", - "General": "Общие", - "Geometry": "Геометрия", - "Get Develop Settings": "Получить настройки коррекции", - "Global Hue": "Общий тон", - "Global Luminance": "Общая яркость", - "Global Saturation": "Общая насыщенность", - "Glossy": "Глянцевый", - "Go to History Step": "Перейти к шагу истории", - "Golden": "Золотой", - "Golden Glow": "Золотое сияние", - "Golden Hour": "Золотой час", - "Grain": "Зерно", - "Graphic": "Графичный", - "Green": "Зелёный", - "Green Amount": "Количество зелёного", - "Green Hue": "Тон зелёного", - "Green Hue High": "Тон зелёного (верх)", - "Green Hue Low": "Тон зелёного (низ)", - "Green Luminance": "Яркость зелёного", - "Green Primary": "Основной зелёный", - "Green Saturation": "Насыщенность зелёного", - "Green channel": "Зелёный канал", - "Grid": "Сетка", - "Grid Info": "Информация сетки", - "Gritty Street": "Жёсткая улица", - "Group": "Группа", - "Group by Date: Automatic": "Группировка по дате: автоматически", - "Group by Day": "Группировать по дням", - "Group by Month": "Группировать по месяцам", - "Group by Year": "Группировать по годам", - "Group by date": "Группировать по дате", - "Group into Stack": "Сгруппировать в стопку", - "Guided": "Направляемый", - "Guided Upright": "Направляемое выравнивание", - "Guided Upright Guides": "Направляющие выравнивания", - "HDR Merge": "Слияние HDR", - "HDR Panorama Merge": "Слияние HDR-панорамы", - "HDR Panorama with Last Settings": "HDR-панорама с последними настройками", - "HDR Panorama…": "HDR-панорама…", - "HDR with Last Settings": "HDR с последними настройками", - "HDR…": "HDR…", - "Heal": "Лечение", - "Heal the small, soft dark spots sensor dust leaves on smooth areas": "Убирать мелкие мягкие тёмные пятна от пыли на матрице на ровных участках", - "Height": "Высота", - "Height (px)": "Высота (px)", - "Help": "Справка", - "Hide LightCraft": "Скрыть LightCraft", - "Hide Mask": "Скрыть маску", - "Hide Others": "Скрыть остальные", - "Hides this shortcut only; the folder and its photos stay as they are": "Скрывает только этот ярлык; папка и её фото остаются как есть", - "High": "Высокий", - "High Contrast B&W": "Контрастное Ч/Б", - "Highlight": "Свет", - "Highlights": "Света", - "Highlights Hue": "Тон светов", - "Highlights Luminance": "Яркость светов", - "Highlights Saturation": "Насыщенность светов", - "Highlights split": "Разделение светов", - "Histogram": "Гистограмма", - "History": "История", - "History & Activity (Y)": "История и активность (Y)", - "Horizontal": "По горизонтали", - "Horizontal text": "Горизонтальный текст", - "Hue": "Тон", - "Hue Range": "Диапазон тонов", - "If file exists": "Если файл существует", - "Image": "Изображение", - "Import": "Импорт", - "Import Date": "Дата импорта", - "Import Photos": "Импорт фото", - "Import Photos…": "Импорт фото…", - "Import Point Curve Presets": "Импортировать пресеты точечной кривой", - "Import Point Curve Presets…": "Импортировать пресеты точечной кривой…", - "Import Presets": "Импортировать пресеты", - "Import Presets…": "Импортировать пресеты…", - "Import Profiles": "Импортировать профили", - "Import Profiles & Presets…": "Импортировать профили и пресеты…", - "Import from Device": "Импорт с устройства", - "Import from Folder…": "Импорт из папки…", - "Import photos with File → Import Photos… (Cmd+Shift+I), or drop them here": "Импортируйте фото через Файл → Импорт фото… (Cmd+Shift+I) или перетащите их сюда", - "Imported Photos": "Импортированные фото", - "Importing": "Импорт", - "Importing scans the source for photos; it doesn't add the folder to Local (use Local → Browse Folder… to work in a folder without importing).": "При импорте источник сканируется на фото; папка не добавляется в «Локальное» (чтобы работать с папкой без импорта, используйте Локальное → Выбрать папку…).", - "In Recently Deleted": "В «Недавно удалённых»", - "In library": "В библиотеке", - "In memory — nothing is saved": "В памяти — ничего не сохраняется", - "In use": "Используется", - "In-memory session: these settings last until LightCraft quits.": "Сеанс в памяти: эти настройки действуют до закрытия LightCraft.", - "Include subfolders": "Включая вложенные папки", - "Increase Brush Feather": "Увеличить растушёвку кисти", - "Increase Brush Size": "Увеличить размер кисти", - "Info": "Информация", - "Info (I)": "Информация (I)", - "Info overlay": "Информационная накладка", - "Inspect Photo": "Просмотр фото", - "Instant ’70s": "Мгновенный стиль 70-х", - "Interface": "Интерфейс", - "Interface language": "Язык интерфейса", - "Intersect": "Пересечение", - "Intersect with": "Пересечь с", - "Into one folder": "В одну папку", - "Invert": "Инвертировать", - "Invert Mask": "Инвертировать маску", - "JPEG (Large)": "JPEG (большой)", - "JPEG (Small)": "JPEG (малый)", - "Join the ArtCraft Discord": "Присоединяйтесь к Discord ArtCraft", - "Join the ArtCraft Discord…": "Присоединиться к Discord ArtCraft…", - "Keep in Local": "Оставить в «Локальном»", - "Keep it on a drive with room: smart previews are about 1 MB per photo. The thumbnail cache stays in the library.": "Храните их на диске со свободным местом: смарт-просмотр занимает около 1 МБ на фото. Кэш миниатюр остаётся в библиотеке.", - "Keyboard Shortcuts": "Сочетания клавиш", - "Keyboard shortcuts": "Сочетания клавиш", - "Keyword": "Ключевое слово", - "Keyword Painter": "Кисть ключевых слов", - "Keyword Set": "Набор ключевых слов", - "Keyword Sets": "Наборы ключевых слов", - "Keyword Suggestions": "Подсказки ключевых слов", - "Keyword to paint": "Ключевое слово для нанесения", - "Keywords": "Ключевые слова", - "Keywords (K)": "Ключевые слова (K)", - "Keywords you add appear here; ⌥1–⌥9 apply them.": "Добавленные вами ключевые слова появятся здесь; ⌥1–⌥9 применяют их.", - "Landscape": "Пейзаж", - "Language": "Язык", - "Largest long edge the Detail view renders at; larger is sharper on big displays but slower.": "Максимальная длинная сторона, с которой рисуется режим «Детализация»; больше — чётче на больших экранах, но медленнее.", - "Last": "Последний", - "Last view": "Последний вид", - "Lateral Chromatic Aberration": "Боковая хроматическая аберрация", - "Layer": "Слой", - "Layers": "Слои", - "Leave them": "Оставить их", - "Lens": "Объектив", - "Lens Corrections": "Коррекция объектива", - "Level": "Уровень", - "Library": "Библиотека", - "Library Info": "Сведения о библиотеке", - "Library Originals": "Оригиналы библиотеки", - "Library Preferences": "Настройки библиотеки", - "Library State": "Состояние библиотеки", - "Licence:": "Лицензия:", - "Light": "Светлый", - "LightCraft Default": "По умолчанию в LightCraft", - "LightCraft Help": "Справка LightCraft", - "LightCraft Preset": "Пресет LightCraft", - "LightCraft Presets": "Пресеты LightCraft", - "LightCraft Website": "Сайт LightCraft", - "LightCraft couldn't find where to keep your library.": "LightCraft не удалось найти место для хранения вашей библиотеки.", - "LightCraft couldn't open the library at": "LightCraft не удалось открыть библиотеку в", - "LightCraft on GitHub": "LightCraft на GitHub", - "LightCraft website": "Сайт LightCraft", - "LightCraft — Second Window": "LightCraft — второе окно", - "Lights": "Светлые тона", - "Limit file size (KB, 0 = off)": "Ограничить размер файла (КБ, 0 = выкл.)", - "Linear Gradient": "Линейный градиент", - "List Albums": "Список альбомов", - "List Develop Controls": "Список элементов управления коррекцией", - "List History": "Список истории", - "List Presets": "Список пресетов", - "List Profiles": "Список профилей", - "List this folder in Local from now on": "Показывать эту папку в «Локальном» и дальше", - "Local": "Локальное", - "Local folders": "Локальные папки", - "Local library — no cloud account needed": "Локальная библиотека — учётная запись в облаке не нужна", - "Locate Missing File…": "Указать отсутствующий файл…", - "Locate Photo": "Найти фото", - "Location": "Местоположение", - "Long Edge": "Длинная сторона", - "Looking for photos…": "Поиск фото…", - "Lossless": "Без потерь", - "Low": "Низкий", - "Luminance": "Яркость", - "Luminance Range": "Диапазон яркости", - "Lush Greens": "Сочная зелень", - "Magenta": "Пурпурный", - "Magenta Hue": "Тон пурпурного", - "Magenta Luminance": "Яркость пурпурного", - "Magenta Saturation": "Насыщенность пурпурного", - "Make Candidate the Select": "Сделать кандидата выбранным", - "Manual": "Вручную", - "Marquee zoom": "Масштаб рамкой", - "Masking": "Маски", - "Masking (M)": "Маски (M)", - "Masks": "Маски", - "Match": "Совпадение", - "Match Total Exposures": "Совпадение общей экспозиции", - "Matte": "Матовый", - "Matte Soft": "Мягкий матовый", - "Med": "Средний", - "Medium Contrast": "Средний контраст", - "Megapixels": "Мегапиксели", - "Memory Budget": "Лимит памяти", - "Memory Usage": "Использование памяти", - "Memory for caches": "Память для кэшей", - "Menu": "Меню", - "Merge": "Слияние", - "Merge Keywords": "Объединить ключевые слова", - "Merge into…": "Объединить с…", - "Merging…": "Слияние…", - "Meta's terms, not LightCraft's. Downloading it means accepting them.": "Условия Meta, а не LightCraft. Загрузка означает их принятие.", - "Metadata": "Метаданные", - "Metadata Preset": "Пресет метаданных", - "Metadata Presets": "Пресеты метаданных", - "Metadata preset": "Пресет метаданных", - "Midpoint": "Средняя точка", - "Midtones": "Средние тона", - "Midtones Hue": "Тон средних тонов", - "Midtones Luminance": "Яркость средних тонов", - "Midtones Saturation": "Насыщенность средних тонов", - "Midtones split": "Разделение средних тонов", - "Minimize": "Свернуть", - "Missing Photos": "Отсутствующие фото", - "Misty Morning": "Туманное утро", - "Mode": "Режим", - "Modified Date": "Дата изменения", - "Monday": "Понедельник", - "Mono Red Filter": "Моно с красным фильтром", - "Mono Rich": "Насыщенное моно", - "Mono Soft": "Мягкое моно", - "Monochrome": "Монохром", - "Month": "Месяц", - "Moody": "Атмосферный", - "Moody Portrait": "Атмосферный портрет", - "More": "Ещё", - "More preset options": "Другие параметры пресета", - "Move": "Переместить", - "Move Album": "Переместить альбом", - "Move Down in Stack": "Опустить в стопке", - "Move Folder": "Переместить папку", - "Move Folder To…": "Переместить папку в…", - "Move Mask": "Переместить маску", - "Move Preset to Group": "Переместить пресет в группу", - "Move Up in Stack": "Поднять в стопке", - "Move the files (into the library's Originals/, or a folder you choose), removing them from the source": "Переместить файлы (в папку «Originals/» библиотеки или в выбранную папку), удалив их из источника", - "Move them": "Переместить их", - "Move to": "Переместить в", - "Move to Group": "Переместить в группу", - "Move: the files are moved to the folder below; each original (and its XMP sidecar) is removed from the source only after its copy is verified. Duplicates and files that fail stay where they are.": "Перемещение: файлы перемещаются в указанную ниже папку; каждый оригинал (и его XMP-файл) удаляется из источника только после проверки копии. Дубликаты и файлы с ошибками остаются на месте.", - "Muted": "Приглушённый", - "Muted Film": "Приглушённая плёнка", - "My Photos": "Мои фото", - "My Photos Panel": "Панель «Мои фото»", - "Name": "Название", - "Named": "Именованные", - "Names appear in the label menu, the filter bar and the Info panel, and are written to XMP. Empty = the colour's name.": "Названия показываются в меню меток, на панели фильтров и в панели «Информация» и записываются в XMP. Пусто = название цвета.", - "Navigator": "Навигатор", - "Needs Work": "Требует доработки", - "Neon Dusk": "Неоновые сумерки", - "Neon Night": "Неоновая ночь", - "Neutral": "Нейтральный", - "Never": "Никогда", - "New": "Новый", - "New Album": "Новый альбом", - "New Album…": "Новый альбом…", - "New Folder…": "Новая папка…", - "New Group…": "Новая группа…", - "New Smart Album": "Новый смарт-альбом", - "New Smart Album from Filter…": "Новый смарт-альбом из фильтра…", - "New Smart Album…": "Новый смарт-альбом…", - "New album": "Новый альбом", - "New album…": "Новый альбом…", - "New blank PDF": "Новый пустой PDF", - "New name": "Новое имя", - "New…": "Новый…", - "Next Photo": "Следующее фото", - "Night Blue": "Ночной синий", - "No": "Нет", - "No Camera or Card Found": "Камера или карта не найдены", - "No Date Groups": "Без групп по датам", - "No Stars": "Без звёзд", - "No Tool": "Нет инструмента", - "No automatic versions.": "Автоматических версий нет.", - "No camera": "Нет камеры", - "No date": "Нет даты", - "No dust spots found": "Пылинки не найдены", - "No edits yet.": "Правок пока нет.", - "No exposure information": "Нет данных об экспозиции", - "No filter presets yet": "Пресетов фильтров пока нет", - "No lens data embedded in this file (DNG lens opcodes only).": "В этом файле нет данных об объективе (только опкоды объектива DNG).", - "No masks yet. Choose a mask type above.": "Масок пока нет. Выберите тип маски выше.", - "No matching photos": "Нет подходящих фото", - "No metadata found": "Метаданные не найдены", - "No new presets": "Новых пресетов нет", - "No photo selected": "Фото не выбрано", - "No photos": "Нет фото", - "No photos found": "Фото не найдены", - "No photos in this folder": "В этой папке нет фото", - "No photos match the active filters": "Нет фото, соответствующих активным фильтрам", - "No photos match the filters (View → Clear Filters)": "Нет фото, соответствующих фильтрам (Вид → Сбросить фильтры)", - "No raw photos yet: cameras appear here once their photos are in the library.": "RAW-фото пока нет: камеры появятся здесь, когда их фото попадут в библиотеку.", - "No reference photo": "Нет эталонного фото", - "No usable GPU found: rendering on the CPU": "Подходящий GPU не найден: рендеринг на CPU", - "No versions yet. Create one to keep this look.": "Версий пока нет. Создайте версию, чтобы сохранить этот вид.", - "Noise": "Шум", - "Noise Reduction": "Подавление шума", - "Non-raw photos": "Не RAW-фото", - "None": "Нет", - "Nothing to compare": "Нечего сравнивать", - "Nothing to survey": "Нечего просматривать в обзоре", - "Nudge Develop Value": "Подправить значение коррекции", - "OK": "OK", - "Object": "Объект", - "Off": "Выкл.", - "Offset X": "Смещение по X", - "Offset Y": "Смещение по Y", - "Opacity": "Непрозрачность", - "Opacity (%)": "Непрозрачность (%)", - "Open Library": "Открыть библиотеку", - "Open Library…": "Открыть библиотеку…", - "Open in": "Открыть в", - "Open in Detail": "Открыть в режиме «Детализация»", - "Open the place in OpenStreetMap": "Открыть место в OpenStreetMap", - "Open…": "Открыть…", - "Optics": "Оптика", - "Optimize Library": "Оптимизировать библиотеку", - "Optional name": "Название (необязательно)", - "Options": "Параметры", - "Orange": "Оранжевый", - "Orange Hue": "Тон оранжевого", - "Orange Luminance": "Яркость оранжевого", - "Orange Saturation": "Насыщенность оранжевого", - "Orientation": "Ориентация", - "Original": "Оригинал", - "Original + Settings": "Оригинал + настройки", - "Original files": "Оригиналы файлов", - "Original offline · editing the smart preview": "Оригинал недоступен · правка смарт-просмотра", - "Original offline · no smart preview": "Оригинал недоступен · нет смарт-просмотра", - "Originals": "Оригиналы", - "Other images (JPEG, PNG, TIFF, HEIC…)": "Другие изображения (JPEG, PNG, TIFF, HEIC…)", - "Overlay": "Наложение", - "Overwrite": "Перезаписать", - "Pages": "Страницы", - "Paint": "Закрасить", - "Paint only areas like the one under the brush": "Закрашивать только области, похожие на ту, что под кистью", - "Paint over a distraction on the photo to remove it.": "Закрасьте лишнее на фото, чтобы убрать это.", - "Paint overlay": "Наложение закраски", - "Panorama Merge": "Слияние панорамы", - "Panorama with Last Settings": "Панорама с последними настройками", - "Panorama…": "Панорама…", - "Parametric curve": "Параметрическая кривая", - "Paste": "Вставить", - "Paste Edit Settings": "Вставить настройки правки", - "Paste Metadata": "Вставить метаданные", - "Paste Selected Settings": "Вставить выбранные настройки", - "Paste Selected Settings…": "Вставить выбранные настройки…", - "Paste Settings from Previous": "Вставить настройки из предыдущего", - "Paste pages": "Вставить страницы", - "Pastel Haze": "Пастельная дымка", - "Percent": "Проценты", - "Percentage": "Процент", - "Performance": "Производительность", - "Perspective": "Перспектива", - "Pet Eye": "Глаза питомца", - "Pet Eye Catchlight": "Блик в глазах питомца", - "Photo": "Фото", - "Photo Grid": "Сетка фото", - "Photo Grid (G)": "Сетка фото (G)", - "Photo Merge": "Слияние фото", - "Photo ▸ Edit in External Editor (⇧⌘E) renders a 16-bit TIFF copy, stacks it with the original and opens it here (an app name on macOS, a program path elsewhere).": "Фото ▸ Править во внешнем редакторе (⇧⌘E) создаёт 16-битную TIFF-копию, помещает её в стопку с оригиналом и открывает здесь (имя приложения в macOS, путь к программе в других системах).", - "Photos": "Фото", - "Photos seen in Local but never added or changed leave the catalog when their folder hasn't been browsed for this long (checked when the library opens). Files stay on disk; browsing the folder shows them again.": "Фото, которые видели в «Локальном», но ни разу не добавляли и не меняли, исключаются из каталога, если папку не открывали так долго (проверяется при открытии библиотеки). Файлы остаются на диске; при просмотре папки они появятся снова.", - "Photos that arrive in this folder (tethering, a scanner, a sync app) are added as soon as they're complete.": "Фото, поступающие в эту папку (тетеринг, сканер, приложение синхронизации), добавляются, как только будут готовы.", - "Pick": "Выбрать", - "Pick a colour on the photo with the eyedropper.": "Выберите цвет на фото пипеткой.", - "Pick the sharpest photo of each burst": "Выбирать самое чёткое фото в каждой серии", - "Picked": "Выбрано", - "Picks": "Выбранные", - "Pixels": "Пиксели", - "Point Color": "Точечный цвет", - "Point Curve": "Точечная кривая", - "Point Curve Presets": "Пресеты точечной кривой", - "Point curve": "Точечная кривая", - "Portrait": "Портрет", - "Position": "Положение", - "Preferences": "Настройки", - "Preferences…": "Настройки…", - "Preset": "Пресет", - "Preset name": "Название пресета", - "Presets": "Пресеты", - "Presets & Profiles": "Пресеты и профили", - "Presets (Shift+P)": "Пресеты (Shift+P)", - "Presets ▾": "Пресеты ▾", - "Preview": "Просмотр", - "Preview Build Progress": "Ход создания просмотров", - "Preview only": "Только просмотр", - "Preview size": "Размер просмотра", - "Previews": "Просмотры", - "Previous Photo": "Предыдущее фото", - "Print": "Печать", - "Print…": "Печать…", - "Profile": "Профиль", - "Profile Browser": "Обозреватель профилей", - "Profile Distortion": "Дисторсия профиля", - "Profile Menu": "Меню профилей", - "Profile Vignetting": "Виньетирование профиля", - "Profile:": "Профиль:", - "Profiles": "Профили", - "Projection": "Проекция", - "Properties": "Свойства", - "Public domain": "Общественное достояние", - "Pupil Size": "Размер зрачка", - "Purple": "Фиолетовый", - "Purple Amount": "Количество фиолетового", - "Purple Hue": "Тон фиолетового", - "Purple Hue High": "Тон фиолетового (верх)", - "Purple Hue Low": "Тон фиолетового (низ)", - "Purple Luminance": "Яркость фиолетового", - "Purple Saturation": "Насыщенность фиолетового", - "Put the locations you removed from Local back (no files change)": "Вернуть в «Локальное» удалённые вами места (файлы не меняются)", - "Quality": "Качество", - "Query Photos": "Запросить фото", - "Quick Collection": "Быстрая коллекция", - "Quick Develop": "Быстрая коррекция", - "Quiet Green": "Тихий зелёный", - "Quit": "Выход", - "Quit LightCraft": "Выйти из LightCraft", - "Radial Gradient": "Радиальный градиент", - "Radius": "Радиус", - "Random": "Случайно", - "Range": "Диапазон", - "Rating": "Оценка", - "Ratings & flags": "Оценки и флаги", - "Raw default": "Стандарт RAW", - "Raw defaults": "Стандарты RAW", - "Raw files": "RAW-файлы", - "Raw photos": "RAW-фото", - "Raw photos as DNG, with the edits embedded. Size, color and output options don't apply.": "RAW-фото как DNG со встроенными правками. Параметры размера, цвета и вывода не применяются.", - "Read Metadata from File": "Прочитать метаданные из файла", - "Read mode": "Режим чтения", - "Read the SAM License": "Прочитать лицензию SAM", - "Reading": "Чтение", - "Reading folder…": "Чтение папки…", - "Recently Added": "Недавно добавленные", - "Recently Deleted": "Недавно удалённые", - "Red": "Красный", - "Red Eye": "Красные глаза", - "Red Hue": "Тон красного", - "Red Luminance": "Яркость красного", - "Red Primary": "Основной красный", - "Red Saturation": "Насыщенность красного", - "Red channel": "Красный канал", - "Red/Cyan Fringe": "Красно-голубая кайма", - "Redo": "Повторить", - "Reference": "Эталон", - "Reference View": "Режим эталона", - "Reference the files where they are": "Ссылаться на файлы там, где они лежат", - "Refine Mask Edges": "Уточнить края маски", - "Refresh Source": "Обновить источник", - "Refresh Source (/)": "Обновить источник (/)", - "Reject": "Отклонить", - "Reject below focus": "Отклонять при недостаточной резкости", - "Rejected": "Отклонено", - "Rejects": "Отклонённые", - "Reload from Disk": "Перезагрузить с диска", - "Remove": "Удалить", - "Remove (H)": "Удалить (H)", - "Remove CA": "Убрать ХА", - "Remove Chromatic Aberration": "Убрать хроматические аберрации", - "Remove from Album": "Удалить из альбома", - "Remove from Favorites": "Удалить из избранного", - "Remove from Local": "Убрать из «Локального»", - "Remove from Photo": "Убрать с фото", - "Remove from Selected Photos": "Убрать с выбранных фото", - "Remove from Stack": "Убрать из стопки", - "Remove from survey": "Убрать из обзора", - "Remove location info": "Удалить сведения о местоположении", - "Remove this filter": "Убрать этот фильтр", - "Remove this group": "Убрать эту группу", - "Remove this rule": "Убрать это условие", - "Rename": "Переименовать", - "Rename Album": "Переименовать альбом", - "Rename Folder": "Переименовать папку", - "Rename Folder…": "Переименовать папку…", - "Rename Keyword": "Переименовать ключевое слово", - "Rename Keyword…": "Переименовать ключевое слово…", - "Rename Mask": "Переименовать маску", - "Rename Photos": "Переименовать фото", - "Rename Photos…": "Переименовать фото…", - "Rename Preset": "Переименовать пресет", - "Rename Preview": "Предпросмотр переименования", - "Rename Template Tags": "Теги шаблона переименования", - "Rename Version": "Переименовать версию", - "Rename…": "Переименовать…", - "Rendering": "Рендеринг", - "Rendering…": "Рендеринг…", - "Reset": "Сбросить", - "Reset All Curves": "Сбросить все кривые", - "Reset Before to Import State": "Сбросить «до» к состоянию при импорте", - "Reset Control": "Сбросить элемент управления", - "Reset Crop": "Сбросить кадрирование", - "Reset Edits": "Сбросить правки", - "Reset Parametric Curve": "Сбросить параметрическую кривую", - "Reset Section": "Сбросить раздел", - "Reset Tone Curve": "Сбросить тоновую кривую", - "Reset all curves: point curves (all channels) and parametric (double-click a channel to reset only that one)": "Сбросить все кривые: точечные (все каналы) и параметрическую (двойной щелчок по каналу сбрасывает только его)", - "Reset all edits (Cmd+Shift+R)": "Сбросить все правки (Cmd+Shift+R)", - "Reshuffle": "Перемешать", - "Resize to": "Изменить размер до", - "Resolution (ppi)": "Разрешение (ppi)", - "Restore": "Восстановить", - "Restore Library from Backup…": "Восстановить библиотеку из резервной копии…", - "Restore Version": "Восстановить версию", - "Retouch": "Ретушь", - "Revert": "Вернуть", - "Review Import": "Проверить импорт", - "Right-click a photo ▸ Set as Reference Photo, then View → Reference View (Shift+R)": "Щёлкните фото правой кнопкой ▸ Назначить эталонным фото, затем Вид → Режим эталона (Shift+R)", - "Right-click a preset to delete it": "Щёлкните пресет правой кнопкой, чтобы удалить его", - "Rights Usage Terms": "Условия использования прав", - "Rotate": "Повернуть", - "Rotate Crop Aspect": "Повернуть пропорции кадра", - "Rotate Left": "Повернуть влево", - "Rotate Right": "Повернуть вправо", - "Roughness": "Шероховатость", - "Roundness": "Округлость", - "SAM 3 download stopped: it resumes where it left off next time.": "Загрузка SAM 3 остановлена: в следующий раз она продолжится с того же места.", - "Same as raw default": "Как стандарт RAW", - "Sample Mask Color": "Взять образец цвета маски", - "Sample a colour on the photo": "Взять образец цвета на фото", - "Saturation": "Насыщенность", - "Saturation Range": "Диапазон насыщенности", - "Saturday": "Суббота", - "Save": "Сохранить", - "Save As…": "Сохранить как…", - "Save Color Label Set": "Сохранить набор цветовых меток", - "Save Current Filter as Preset…": "Сохранить текущий фильтр как пресет…", - "Save Current Keywords as Set…": "Сохранить текущие ключевые слова как набор…", - "Save Export Preset": "Сохранить пресет экспорта", - "Save Filter Preset": "Сохранить пресет фильтра", - "Save Keyword Set": "Сохранить набор ключевых слов", - "Save Metadata Preset": "Сохранить пресет метаданных", - "Save Metadata Preset…": "Сохранить пресет метаданных…", - "Save Metadata to File": "Сохранить метаданные в файл", - "Save Point Curve Preset": "Сохранить пресет точечной кривой", - "Save Point Curve…": "Сохранить точечную кривую…", - "Save as Preset": "Сохранить как пресет", - "Save as Smart Album…": "Сохранить как смарт-альбом…", - "Save as set": "Сохранить как набор", - "Save as…": "Сохранить как…", - "Save preset": "Сохранить пресет", - "Scale": "Масштаб", - "Scanning": "Сканирование", - "Scores stay on the photos: filter or make smart albums with Focus and Best of Similar Shots.": "Оценки остаются на фото: фильтруйте или создавайте смарт-альбомы по Focus и Best of Similar Shots.", - "Screen": "Экран", - "Search Photos": "Поиск фото", - "Search…": "Поиск…", - "Seasons": "Времена года", - "Second Window": "Второе окно", - "Select": "Выбрать", - "Select All": "Выбрать все", - "Select Luminance Range": "Выбрать диапазон яркости", - "Select Mask": "Выбрать маску", - "Select Photos": "Выбрать фото", - "Select Spot": "Выбрать пятно", - "Select a photo to edit": "Выберите фото для правки", - "Select by": "Выбрать по", - "Select by Flag, Rating or Label": "Выбрать по флагу, оценке или метке", - "Select photos, then choose View → Survey (N)": "Выберите фото, затем Вид → Обзор (N)", - "Select two photos, then choose View → Compare (Shift+C)": "Выберите два фото, затем Вид → Сравнение (Shift+C)", - "Selected": "Выделено", - "Selenium Tone": "Селеновое тонирование", - "Send Feedback…": "Отправить отзыв…", - "Sepia Tone": "Сепия", - "Services": "Службы", - "Set Before": "Назначить «до»", - "Set Color Label": "Назначить цветовую метку", - "Set Crop": "Задать кадрирование", - "Set Develop Values": "Задать значения коррекции", - "Set Flag": "Задать флаг", - "Set Mask Adjustments": "Задать настройки маски", - "Set Point Curve": "Задать точечную кривую", - "Set Profile": "Задать профиль", - "Set Rating": "Задать оценку", - "Set as Album Cover": "Сделать обложкой альбома", - "Set as Before": "Назначить как «до»", - "Set as Reference Photo": "Назначить эталонным фото", - "Set as Target Album": "Назначить целевым альбомом", - "Set as Target Album (B adds to it)": "Назначить целевым альбомом (B добавляет в него)", - "Set as Top of Stack": "Сделать верхним в стопке", - "Set the mix from the photo's colours": "Задать микс по цветам фото", - "Settings": "Настройки", - "Settings new raw photos start from. Changing them doesn't touch photos already in the library.": "Настройки, с которых начинаются новые RAW-фото. Их изменение не затрагивает фото, уже находящиеся в библиотеке.", - "Settings to include": "Включаемые настройки", - "Settings…": "Настройки…", - "Shade": "Тень", - "Shadow": "Тень", - "Shadows": "Тени", - "Shadows Hue": "Тон теней", - "Shadows Luminance": "Яркость теней", - "Shadows Saturation": "Насыщенность теней", - "Shadows Tint": "Оттенок теней", - "Shadows split": "Разделение теней", - "Sharpen": "Повысить резкость", - "Sharpening": "Резкость", - "Sharpness": "Резкость", - "Short Edge": "Короткая сторона", - "Show All": "Показать все", - "Show Clipping": "Показать отсечение", - "Show Deghost Overlay": "Показать наложение удаления артефактов", - "Show Hidden Local Locations": "Показать скрытые места в «Локальном»", - "Show Luminance Map": "Показать карту яркости", - "Show Mask": "Показать маску", - "Show Mask Overlay": "Показать наложение маски", - "Show Mask Pins": "Показать булавки масок", - "Show Original": "Показать оригинал", - "Show Photo Counts": "Показывать количество фото", - "Show Photos with Keyword": "Показать фото с ключевым словом", - "Show Pins": "Показать булавки", - "Show Source": "Показать источник", - "Show Thumbnails": "Показывать миниатюры", - "Show destination gamut warning": "Показывать предупреждение о выходе за гамму устройства вывода", - "Show display gamut warning": "Показывать предупреждение о выходе за гамму дисплея", - "Show file names": "Показывать имена файлов", - "Show in Finder": "Показать в Finder", - "Show on Map": "Показать на карте", - "Show overlay (O)": "Показать наложение (O)", - "Show ratings, flags and edit badges": "Показывать оценки, флаги и значки правок", - "Show the Navigator while zoomed in": "Показывать навигатор при увеличении", - "Show the template tags; click one to insert it": "Показать теги шаблона; щёлкните по тегу, чтобы вставить его", - "Show/Hide Mask": "Показать/скрыть маску", - "Show/hide My Photos panel": "Показать/скрыть панель «Мои фото»", - "Shutter speed": "Выдержка", - "Side panels": "Боковые панели", - "Sidecar names": "Имена XMP-файлов", - "Size": "Размер", - "Size (% of short edge)": "Размер (% от короткой стороны)", - "Size limit": "Ограничение размера", - "Skip": "Пропустить", - "Sky": "Небо", - "Slideshow": "Слайд-шоу", - "Smart Album": "Смарт-альбом", - "Smart Album Rule Fields": "Поля условий смарт-альбома", - "Smart Preview Status": "Состояние смарт-просмотров", - "Smart Previews Location": "Расположение смарт-просмотров", - "Smart previews": "Смарт-просмотры", - "Smoothness": "Плавность", - "Soft B&W": "Мягкое Ч/Б", - "Soft Pastel": "Мягкий пастельный", - "Soft Proofing": "Экранная цветопроба", - "Soft Skin": "Мягкая кожа", - "Sort": "Сортировка", - "Sort by": "Сортировать по", - "Source": "Источник", - "Source code on GitHub": "Исходный код на GitHub", - "Spherical": "Сферическая", - "Spiral": "Спираль", - "Split Stack": "Разделить стопку", - "Spring Fresh": "Весенняя свежесть", - "Square Grid": "Квадратная сетка", - "Square Grid (G toggles)": "Квадратная сетка (G переключает)", - "Square Grid: show file names and formats": "Квадратная сетка: показывать имена и форматы файлов", - "Stack": "Стопка", - "Stack photos taken within this time of each other:": "Объединять в стопку фото, снятые с интервалом не более:", - "Stacks": "Стопки", - "Standard": "Стандартный", - "Start at": "Начать с", - "Start number": "Начальный номер", - "Startup": "Запуск", - "State / Province": "Область / регион", - "Stop": "Остановить", - "Stop Building Previews": "Остановить создание просмотров", - "Stop Using as Target Album": "Больше не использовать как целевой альбом", - "Stopping…": "Остановка…", - "Straighten": "Выровнять", - "Straighten Tool": "Инструмент «Выпрямление»", - "Strong Contrast": "Сильный контраст", - "Style": "Стиль", - "Subfolder": "Подпапка", - "Subfolder name": "Название подпапки", - "Subject": "Главный объект", - "Subtract": "Вычесть", - "Subtract from this mask": "Вычесть из этой маски", - "Suggestions": "Подсказки", - "Summer Haze": "Летняя дымка", - "Sunday": "Воскресенье", - "Survey": "Обзор", - "Survey (N)": "Обзор (N)", - "Swap": "Поменять", - "Swap Aspect (X)": "Поменять пропорции (X)", - "Swap Before and After Settings": "Поменять местами настройки «до» и «после»", - "Swap Compare Photos": "Поменять местами фото для сравнения", - "Switch light / dark theme": "Переключить светлую/тёмную тему", - "Sync Settings": "Синхронизировать настройки", - "System Info": "Сведения о системе", - "System Info…": "Сведения о системе…", - "System default": "Как в системе", - "Tags": "Теги", - "Tags: see Tags beside the template. Files are renamed on disk (with their XMP sidecars); existing names get -1, -2…": "Теги: см. «Теги» рядом с шаблоном. Файлы переименовываются на диске (вместе с XMP-файлами); к существующим именам добавляется -1, -2…", - "Take a snapshot": "Сделать снимок", - "Targeted Adjustment": "Целевая коррекция", - "Targeted adjustment: drag up/down on the photo to adjust the colours there": "Целевая коррекция: перетаскивайте вверх/вниз по фото, чтобы менять цвета в этом месте", - "Targeted adjustment: drag up/down on the photo to adjust the curve there": "Целевая коррекция: перетаскивайте вверх/вниз по фото, чтобы менять кривую в этом месте", - "Teal & Amber": "Бирюзовый и янтарный", - "Teal & Orange": "Бирюзовый и оранжевый", - "Temp": "Темп.", - "Temperature": "Температура", - "Template": "Шаблон", - "Temporary session — your library isn't open, and nothing you do here is saved.": "Временный сеанс — ваша библиотека не открыта, и ничего из сделанного здесь не сохраняется.", - "Text": "Текст", - "Text direction": "Направление текста", - "Texture": "Текстура", - "The SAM 3 model is installed: Object and Describe masks are ready.": "Модель SAM 3 установлена: маски «Объект» и «Описать» готовы.", - "The download didn't work:": "Загрузка не удалась:", - "The original files, unchanged, each with an XMP sidecar holding its edits.": "Исходные файлы без изменений, у каждого — XMP-файл с его правками.", - "The photo has these settings": "У фото есть эти настройки", - "Theme": "Тема", - "They can be restored from Recently Deleted until it is emptied.": "Их можно восстановить из «Недавно удалённых», пока этот раздел не очищен.", - "Thirds": "Правило третей", - "This folder is not available (drive disconnected?). Smart previews are not built or used until it is back or you choose another folder.": "Эта папка недоступна (диск отключён?). Смарт-просмотры не создаются и не используются, пока она не появится снова или вы не выберете другую папку.", - "This photo can't be opened": "Это фото не удаётся открыть", - "Threshold": "Порог", - "Thumbnail cache": "Кэш миниатюр", - "Thursday": "Четверг", - "Tint": "Оттенок", - "Title": "Заголовок", - "Toggle Keyword from Set": "Переключить ключевое слово из набора", - "Toggle Section": "Свернуть/развернуть раздел", - "Toggle Zoom": "Переключить масштаб", - "Tone Curve": "Тоновая кривая", - "Tools": "Инструменты", - "Top Level": "Верхний уровень", - "Transfer": "Перенос", - "Treatment": "Обработка", - "Triangle": "Треугольник", - "Try Again": "Повторить попытку", - "Tuesday": "Вторник", - "Tungsten": "Лампа накаливания", - "Turn Off": "Выключить", - "Turn on Include subfolders, or pick another folder under Local": "Включите «Включая вложенные папки» или выберите другую папку в «Локальном»", - "Type": "Тип", - "Uncheck All": "Снять все отметки", - "Undo": "Отменить", - "Unedited": "Без правок", - "Unflag": "Снять флаг", - "Unflagged": "Без флага", - "Ungroup Stack": "Разгруппировать стопку", - "Unknown Date": "Дата неизвестна", - "Unrated": "Без оценки", - "Unreadable": "Не читается", - "Update": "Обновить", - "Update Mask Shape": "Обновить форму маски", - "Update Preset with Current Settings": "Обновить пресет текущими настройками", - "Update Rules from Current Filter": "Обновить условия из текущего фильтра", - "Update Version": "Обновить версию", - "Update Version with Current Settings": "Обновить версию текущими настройками", - "Update with Current Settings": "Обновить текущими настройками", - "Upright": "Выравнивание (Upright)", - "Urban": "Урбан", - "Use Keyword Set": "Использовать набор ключевых слов", - "Use Library Folder": "Использовать папку библиотеки", - "Use camera-specific defaults": "Использовать настройки для конкретной камеры", - "Use the GPU for rendering": "Использовать GPU для рендеринга", - "User Presets": "Пользовательские пресеты", - "Value": "Значение", - "Variance": "Разброс", - "Version": "Версия", - "Version name": "Название версии", - "Versions": "Версии", - "Versions (Shift+V)": "Версии (Shift+V)", - "Vertical": "По вертикали", - "Vertical text": "Вертикальный текст", - "Vibrance": "Красочность", - "Videos": "Видео", - "View": "Вид", - "Vignette": "Виньетка", - "Vignetting": "Виньетирование", - "Vintage": "Винтаж", - "Virtual Copy": "Виртуальная копия", - "Visible": "Видимость", - "Visualize Spots": "Визуализировать пятна", - "Visualize Spots (A)": "Визуализировать пятна (A)", - "Visualize range": "Показать диапазон", - "Vivid": "Яркий", - "Vivid Pop": "Яркий поп", - "Warm Film": "Тёплая плёнка", - "Warm Glow": "Тёплое сияние", - "Warm Print": "Тёплый отпечаток", - "Warm Table": "Тёплый стол", - "Watched folder": "Отслеживаемая папка", - "Watermark": "Водяной знак", - "Wednesday": "Среда", - "What's New": "Что нового", - "When rated or hovered": "При оценке или наведении", - "White Balance": "Баланс белого", - "White Balance Selector": "Пипетка баланса белого", - "White Balance Selector (W)": "Пипетка баланса белого (W)", - "Whites": "Белые", - "Width": "Ширина", - "Width (% of photo)": "Ширина (% от фото)", - "Width (px)": "Ширина (px)", - "Width × Height": "Ширина × высота", - "Window": "Окно", - "Winter Blue": "Зимняя синева", - "XMP Sidecar Preferences": "Настройки XMP-файлов", - "XMP sidecars": "XMP-файлы", - "Year": "Год", - "Yellow": "Жёлтый", - "Yellow Hue": "Тон жёлтого", - "Yellow Luminance": "Яркость жёлтого", - "Yellow Saturation": "Насыщенность жёлтого", - "Yes": "Да", - "You can close this: the download continues, and resumes if interrupted.": "Это окно можно закрыть: загрузка продолжится и возобновится после обрыва.", - "Your library couldn't be opened": "Не удалось открыть вашу библиотеку", - "Zoom": "Масштаб", - "Zoom 100%": "Масштаб 100%", - "Zoom In": "Увеличить", - "Zoom Out": "Уменьшить", - "Zoom to Fit": "Масштаб по размеру окна", - "Zoom to page level": "Масштаб до уровня страницы", - "and": "и", - "comma, separated": "через запятую", - "memory only": "только в памяти", - "none": "нет", - "not available here": "здесь недоступно", - "of the following rules:": "из следующих условий:", - "saved in": "сохранено в", - "this photo": "это фото", - "− harder border · + softer border": "− жёстче край · + мягче край" -} From 11d6676984ca76f54abc6aad97e1e5faa1d3145c Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:15:15 +0400 Subject: [PATCH 014/213] Delete crates/ui-egui/locales/ru-formats.json --- crates/ui-egui/locales/ru-formats.json | 122 ------------------------- 1 file changed, 122 deletions(-) delete mode 100644 crates/ui-egui/locales/ru-formats.json diff --git a/crates/ui-egui/locales/ru-formats.json b/crates/ui-egui/locales/ru-formats.json deleted file mode 100644 index 9e2c96dd3..000000000 --- a/crates/ui-egui/locales/ru-formats.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - " ({} in Recently Deleted)": " ({} в «Недавно удалённых»)", - " · {skipped} skipped (file exists)": " · пропущено: {skipped} (файл существует)", - " · {} duplicate{} skipped": " · пропущено дубликатов: {}{:.0}", - " · {} failed: {}": " · не удалось: {}; ошибка: {}", - " · {} moved": " · перемещено: {}", - " · {} not readable": " · не читается: {}", - ", {failed} file{} not readable": ", не читается файлов: {failed}{:.0}", - "Add {local_n} to My Photos": "Добавить в «Мои фото»: {local_n}", - "Add {} Photo{}": "Добавить фото: {}{:.0}", - "Add “{}” to Favorites": "Добавить «{}» в избранное", - "Added {n} photo{} to My Photos": "В «Мои фото» добавлено фото: {n}{:.0}", - "Added {n} photo{} to “{}”": "В «{1}» добавлено фото: {n}{0:.0}", - "Adding photos… {} of {}": "Добавление фото… {} из {}", - "All {n} selected photos move by the same amount.": "Все выбранные фото ({n}) сдвигаются на одно и то же значение.", - "Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "Уже в библиотеке, в «Недавно удалённых»: выбрано фото: {}{:.0} · щёлкните правой кнопкой ▸ Восстановить или Удалить навсегда, чтобы импортировать заново", - "Already in the library: {} photo{} selected in All Photos": "Уже в библиотеке: выбрано фото в разделе «Все фото»: {}{:.0}", - "Auto ({})": "Авто ({})", - "Auto Sync: edits apply to {n} photos": "Автосинхронизация: правки применяются к фото: {n}", - "Automatic ({} MB)": "Автоматически ({} МБ)", - "Build 1:1 Previews ({scope})": "Создать просмотры 1:1 ({scope})", - "Build Smart Previews ({scope})": "Создать смарт-просмотры ({scope})", - "Build Standard-Sized Previews ({scope})": "Создать просмотры стандартного размера ({scope})", - "Click {}:1": "Щелчок {}:1", - "Create {n} Virtual Copies": "Создать виртуальные копии: {n}", - "Decoder: {reason}": "Декодер: {reason}", - "Delete \"{label}\"": "Удалить «{label}»", - "Delete \"{label}\" (and the mask)": "Удалить «{label}» (и маску)", - "Delete {n} Photos": "Удалить фото: {n}", - "Delete “{current}”": "Удалить «{current}»", - "Discard Smart Previews ({scope})": "Удалить смарт-просмотры ({scope})", - "Duplicate \"{label}\"": "Дублировать «{label}»", - "Example: {example}": "Пример: {example}", - "Exported {n} photo{}": "Экспортировано фото: {n}{:.0}", - "Exported {ok} of {total} photo{}": "Экспортировано фото: {ok} из {total}{:.0}", - "Exported {} point curve preset{}": "Экспортировано пресетов точечной кривой: {}{:.0}", - "Exported {} preset{}": "Экспортировано пресетов: {}{:.0}", - "Exporting {} of {total}": "Экспорт: {} из {total}", - "Filter bar — {active} active filter{}": "Панель фильтров — активных фильтров: {active}{:.0}", - "Healed {n} dust spot{}": "Убрано пылинок: {n}{:.0}", - "Import cancelled · {} photo{} added": "Импорт отменён · добавлено фото: {}{:.0}", - "Imported {n} point curve preset{}": "Импортировано пресетов точечной кривой: {n}{:.0}", - "Imported {n} preset{}": "Импортировано пресетов: {n}{:.0}", - "Imported {n} preset{}, {f} file{} not readable": "Импортировано пресетов: {n}{:.0}, не читается файлов: {f}{:.0}", - "Imported {} photo{}": "Импортировано фото: {}{:.0}", - "Label Set: {name}": "Набор меток: {name}", - "Matches: {}": "Совпадения: {}", - "Merging… {stage} {:.0}%": "Слияние… {stage} {:.0}%", - "Move {what} to Recently Deleted?": "Переместить {what} в «Недавно удалённые»?", - "Named ({named_n})": "Именованные ({named_n})", - "Paste into {n} photo{} — only settings that were copied are pasted": "Вставка в фото: {n}{:.0} — вставляются только скопированные настройки", - "Preset: {name}": "Пресет: {name}", - "Preview only — editing the camera's embedded JPEG ({})": "Только просмотр — правка встроенного в камеру JPEG ({})", - "Preview only — {}": "Только просмотр — {}", - "Profile: {}": "Профиль: {}", - "Proof Copy ({})": "Пробная копия ({})", - "Proof Preview · {}": "Пробный просмотр · {}", - "Quick Develop · {n} photos": "Быстрая коррекция · фото: {n}", - "Quick Develop: {name}": "Быстрая коррекция: {name}", - "Rated {}": "Оценка: {}", - "Reading photos… {done} of {total}": "Чтение фото… {done} из {total}", - "Reading photos… {} of {}": "Чтение фото… {} из {}", - "Redo {}": "Повторить {}", - "Remove “{}” from Favorites": "Убрать «{}» из избранного", - "Rename {n} Photos…": "Переименовать фото: {n}…", - "Rename “{name}”": "Переименовать «{name}»", - "Rendering on the CPU: {why}": "Рендеринг на CPU: {why}", - "Reset {name}": "Сбросить: {name}", - "Reset {} Channel": "Сбросить канал: {}", - "Saved export preset “{}”": "Пресет экспорта «{}» сохранён", - "Show {hidden} hidden location{}": "Показать скрытые места: {hidden}{:.0}", - "Showing {n} of {}": "Показано {n} из {}", - "Smart album: {}": "Смарт-альбом: {}", - "Undo {}": "Отменить {}", - "Updated “{name}”": "«{name}» обновлено", - "Version {} — a clean-room, pure-Rust photo library and raw developer.": "Версия {} — независимо разработанная библиотека фото и RAW-конвертер на чистом Rust.", - "Version: {}": "Версия: {}", - "none (CPU rendering): {why}": "нет (рендеринг на CPU): {why}", - "the {n} selected photos": "выбранные {n} фото", - "the {} photos in view": "все {} фото в представлении", - "these {count} photos": "эти {count} фото", - "{count} smart preview{} · {:.1} MB": "Смарт-просмотров: {count}{:.0} · {:.1} МБ", - "{f} folders (and their subfolders)": "Папок: {f} (и их подпапок)", - "{label} Grading": "Цветокоррекция: {label}", - "{label} {d:+} on every selected photo": "{label} {d:+} для каждого выбранного фото", - "{label}, {n} photos": "{label}, фото: {n}", - "{matching} of {t} photos": "Фото: {matching} из {t}", - "{matching} photos": "Фото: {matching}", - "{mode} spot {} of {}": "{mode}: пятно {} из {}", - "{month}": "{month}", - "{month} {year}": "{month:02}.{year}", - "{msg} ({failed} failed)": "{msg} (ошибок: {failed})", - "{name} Label": "Метка: {name}", - "{name} Off": "{name}: выкл.", - "{name} On": "{name}: вкл.", - "{name} — double-click to reset it": "{name} — двойной щелчок для сброса", - "{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.": "Не записано на диск, хранится только в памяти (изменений: {n}): {e}\nLightCraft повторяет попытку автоматически; при выходе сейчас они будут потеряны.{:.0}{:.0}", - "{n} found · {sel} selected": "Найдено: {n} · выбрано: {sel}", - "{n} photos": "Фото: {n}", - "{n} photos in {} are hidden by the filters above": "Фильтры выше скрывают фото в разделе «{}»: {n}", - "{n} photo{}": "Фото: {n}{:.0}", - "{n} photo{} match · updates automatically as photos change": "Подходящих фото: {n}{:.0} · обновляется автоматически при изменении фото", - "{n} photo{} now · updates automatically as photos change": "Сейчас фото: {n}{:.0} · обновляется автоматически при изменении фото", - "{sel_n} selected · {counted}": "Выбрано: {sel_n} · {counted}", - "{stage}… {:.0}%": "{stage}… {:.0}%", - "{verb} {n} Photo{}": "{verb} (фото: {n}){:.0}", - "{weekday}, {day}": "{weekday}, {day}", - "{weekday}, {day} {month} {year}": "{weekday}, {day:02}.{month:02}.{year}", - "{what} merge added": "{what}: слияние добавлено", - "{what} merge failed: {e}": "{what}: ошибка слияния: {e}", - "{year}": "{year}", - "{} and higher": "{} и выше", - "{} files": "Файлов: {}", - "{} files and {f} folder{}": "Файлов: {}, папок: {f}{:.0}", - "{} of 4 guides — drag along lines that should be vertical or horizontal.": "Направляющих: {} из 4 — проведите вдоль линий, которые должны быть вертикальными или горизонтальными.", - "{} photos": "Фото: {}", - "{} photo{} not readable": "Не читается фото: {}{:.0}", - "{} spot(s) on this photo": "Пятен на этом фото: {}", - "· {dups} already in the library": "· уже в библиотеке: {dups}", - "· {n} photo{}": "· фото: {n}{:.0}", - "… and {more} more": "… и ещё {more}" -} From 782ade6c391dd5cb4d8da9f3f831bde021de4832 Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:16:54 +0400 Subject: [PATCH 015/213] Add files via upload --- crates/ui-egui/locales/ru-formats.json | 122 +++ crates/ui-egui/locales/ru.json | 1263 ++++++++++++++++++++++++ 2 files changed, 1385 insertions(+) create mode 100644 crates/ui-egui/locales/ru-formats.json create mode 100644 crates/ui-egui/locales/ru.json diff --git a/crates/ui-egui/locales/ru-formats.json b/crates/ui-egui/locales/ru-formats.json new file mode 100644 index 000000000..2cde4fd9b --- /dev/null +++ b/crates/ui-egui/locales/ru-formats.json @@ -0,0 +1,122 @@ +{ + " ({} in Recently Deleted)": " ({} в «Недавно удалённых»)", + " · {skipped} skipped (file exists)": " · пропущено: {skipped} (файл существует)", + " · {} duplicate{} skipped": " · пропущено дубликатов: {}{:.0}", + " · {} failed: {}": " · не удалось: {}; ошибка: {}", + " · {} moved": " · перемещено: {}", + " · {} not readable": " · не читается: {}", + ", {failed} file{} not readable": ", не читается файлов: {failed}{:.0}", + "Add {local_n} to My Photos": "Добавить в «Мои фото»: {local_n}", + "Add {} Photo{}": "Добавить фото: {}{:.0}", + "Add “{}” to Favorites": "Добавить «{}» в избранное", + "Added {n} photo{} to My Photos": "В «Мои фото» добавлено фото: {n}{:.0}", + "Added {n} photo{} to “{}”": "В «{1}» добавлено фото: {n}{0:.0}", + "Adding photos… {} of {}": "Добавление фото… {} из {}", + "All {n} selected photos move by the same amount.": "Все выбранные фото ({n}) сдвигаются на одно и то же значение.", + "Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "Уже в библиотеке, в «Недавно удалённых»: выбрано фото: {}{:.0} · щёлкните правой кнопкой → Восстановить или Удалить навсегда, чтобы импортировать заново", + "Already in the library: {} photo{} selected in All Photos": "Уже в библиотеке: выбрано фото в разделе «Все фото»: {}{:.0}", + "Auto ({})": "Авто ({})", + "Auto Sync: edits apply to {n} photos": "Автосинхронизация: правки применяются к фото: {n}", + "Automatic ({} MB)": "Автоматически ({} МБ)", + "Build 1:1 Previews ({scope})": "Создать просмотры 1:1 ({scope})", + "Build Smart Previews ({scope})": "Создать смарт-просмотры ({scope})", + "Build Standard-Sized Previews ({scope})": "Создать просмотры стандартного размера ({scope})", + "Click {}:1": "Щелчок {}:1", + "Create {n} Virtual Copies": "Создать виртуальные копии: {n}", + "Decoder: {reason}": "Декодер: {reason}", + "Delete \"{label}\"": "Удалить «{label}»", + "Delete \"{label}\" (and the mask)": "Удалить «{label}» (и маску)", + "Delete {n} Photos": "Удалить фото: {n}", + "Delete “{current}”": "Удалить «{current}»", + "Discard Smart Previews ({scope})": "Удалить смарт-просмотры ({scope})", + "Duplicate \"{label}\"": "Дублировать «{label}»", + "Example: {example}": "Пример: {example}", + "Exported {n} photo{}": "Экспортировано фото: {n}{:.0}", + "Exported {ok} of {total} photo{}": "Экспортировано фото: {ok} из {total}{:.0}", + "Exported {} point curve preset{}": "Экспортировано пресетов точечной кривой: {}{:.0}", + "Exported {} preset{}": "Экспортировано пресетов: {}{:.0}", + "Exporting {} of {total}": "Экспорт: {} из {total}", + "Filter bar — {active} active filter{}": "Панель фильтров — активных фильтров: {active}{:.0}", + "Healed {n} dust spot{}": "Убрано пылинок: {n}{:.0}", + "Import cancelled · {} photo{} added": "Импорт отменён · добавлено фото: {}{:.0}", + "Imported {n} point curve preset{}": "Импортировано пресетов точечной кривой: {n}{:.0}", + "Imported {n} preset{}": "Импортировано пресетов: {n}{:.0}", + "Imported {n} preset{}, {f} file{} not readable": "Импортировано пресетов: {n}{:.0}, не читается файлов: {f}{:.0}", + "Imported {} photo{}": "Импортировано фото: {}{:.0}", + "Label Set: {name}": "Набор меток: {name}", + "Matches: {}": "Совпадения: {}", + "Merging… {stage} {:.0}%": "Слияние… {stage} {:.0}%", + "Move {what} to Recently Deleted?": "Переместить {what} в «Недавно удалённые»?", + "Named ({named_n})": "Именованные ({named_n})", + "Paste into {n} photo{} — only settings that were copied are pasted": "Вставка в фото: {n}{:.0} — вставляются только скопированные настройки", + "Preset: {name}": "Пресет: {name}", + "Preview only — editing the camera's embedded JPEG ({})": "Только просмотр — правка встроенного в камеру JPEG ({})", + "Preview only — {}": "Только просмотр — {}", + "Profile: {}": "Профиль: {}", + "Proof Copy ({})": "Пробная копия ({})", + "Proof Preview · {}": "Пробный просмотр · {}", + "Quick Develop · {n} photos": "Быстрая коррекция · фото: {n}", + "Quick Develop: {name}": "Быстрая коррекция: {name}", + "Rated {}": "Оценка: {}", + "Reading photos… {done} of {total}": "Чтение фото… {done} из {total}", + "Reading photos… {} of {}": "Чтение фото… {} из {}", + "Redo {}": "Повторить {}", + "Remove “{}” from Favorites": "Убрать «{}» из избранного", + "Rename {n} Photos…": "Переименовать фото: {n}…", + "Rename “{name}”": "Переименовать «{name}»", + "Rendering on the CPU: {why}": "Рендеринг на CPU: {why}", + "Reset {name}": "Сбросить: {name}", + "Reset {} Channel": "Сбросить канал: {}", + "Saved export preset “{}”": "Пресет экспорта «{}» сохранён", + "Show {hidden} hidden location{}": "Показать скрытые места: {hidden}{:.0}", + "Showing {n} of {}": "Показано {n} из {}", + "Smart album: {}": "Смарт-альбом: {}", + "Undo {}": "Отменить {}", + "Updated “{name}”": "«{name}» обновлено", + "Version {} — a clean-room, pure-Rust photo library and raw developer.": "Версия {} — независимо разработанная библиотека фото и RAW-конвертер на чистом Rust.", + "Version: {}": "Версия: {}", + "none (CPU rendering): {why}": "нет (рендеринг на CPU): {why}", + "the {n} selected photos": "выбранные {n} фото", + "the {} photos in view": "все {} фото в представлении", + "these {count} photos": "эти {count} фото", + "{count} smart preview{} · {:.1} MB": "Смарт-просмотров: {count}{:.0} · {:.1} МБ", + "{f} folders (and their subfolders)": "Папок: {f} (и их подпапок)", + "{label} Grading": "Цветокоррекция: {label}", + "{label} {d:+} on every selected photo": "{label} {d:+} для каждого выбранного фото", + "{label}, {n} photos": "{label}, фото: {n}", + "{matching} of {t} photos": "Фото: {matching} из {t}", + "{matching} photos": "Фото: {matching}", + "{mode} spot {} of {}": "{mode}: пятно {} из {}", + "{month}": "{month}", + "{month} {year}": "{month:02}.{year}", + "{msg} ({failed} failed)": "{msg} (ошибок: {failed})", + "{name} Label": "Метка: {name}", + "{name} Off": "{name}: выкл.", + "{name} On": "{name}: вкл.", + "{name} — double-click to reset it": "{name} — двойной щелчок для сброса", + "{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.": "Не записано на диск, хранится только в памяти (изменений: {n}): {e}\nLightCraft повторяет попытку автоматически; при выходе сейчас они будут потеряны.{:.0}{:.0}", + "{n} found · {sel} selected": "Найдено: {n} · выбрано: {sel}", + "{n} photos": "Фото: {n}", + "{n} photos in {} are hidden by the filters above": "Фильтры выше скрывают фото в разделе «{}»: {n}", + "{n} photo{}": "Фото: {n}{:.0}", + "{n} photo{} match · updates automatically as photos change": "Подходящих фото: {n}{:.0} · обновляется автоматически при изменении фото", + "{n} photo{} now · updates automatically as photos change": "Сейчас фото: {n}{:.0} · обновляется автоматически при изменении фото", + "{sel_n} selected · {counted}": "Выбрано: {sel_n} · {counted}", + "{stage}… {:.0}%": "{stage}… {:.0}%", + "{verb} {n} Photo{}": "{verb} (фото: {n}){:.0}", + "{weekday}, {day}": "{weekday}, {day}", + "{weekday}, {day} {month} {year}": "{weekday}, {day:02}.{month:02}.{year}", + "{what} merge added": "{what}: слияние добавлено", + "{what} merge failed: {e}": "{what}: ошибка слияния: {e}", + "{year}": "{year}", + "{} and higher": "{} и выше", + "{} files": "Файлов: {}", + "{} files and {f} folder{}": "Файлов: {}, папок: {f}{:.0}", + "{} of 4 guides — drag along lines that should be vertical or horizontal.": "Направляющих: {} из 4 — проведите вдоль линий, которые должны быть вертикальными или горизонтальными.", + "{} photos": "Фото: {}", + "{} photo{} not readable": "Не читается фото: {}{:.0}", + "{} spot(s) on this photo": "Пятен на этом фото: {}", + "· {dups} already in the library": "· уже в библиотеке: {dups}", + "· {n} photo{}": "· фото: {n}{:.0}", + "… and {more} more": "… и ещё {more}" +} diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json new file mode 100644 index 000000000..913c85ad6 --- /dev/null +++ b/crates/ui-egui/locales/ru.json @@ -0,0 +1,1263 @@ +{ + "+ Group": "+ Группа", + "+ Rule": "+ Условие", + "0: nothing is rejected.": "0: ничего не отклоняется.", + "A filter is hiding this view's photos: change it, or clear it (View → Clear Filters)": "Фильтр скрывает фото в этом представлении: измените его или сбросьте (Вид → Сбросить фильтры)", + "A one-time download of about": "Однократная загрузка около", + "About LightCraft": "О программе LightCraft", + "Activity": "Активность", + "Actual size": "Реальный размер", + "Add": "Добавить", + "Add Catchlight": "Добавить блик в глазах", + "Add Folder to Local": "Добавить папку в «Локальное»", + "Add Point Color Sample": "Добавить точечный образец цвета", + "Add Red Eye Correction": "Добавить коррекцию красных глаз", + "Add Remove Spot": "Добавить удаление пятна", + "Add Selected Photos": "Добавить выбранные фото", + "Add a rule (⌥-click: a group)": "Добавить условие (⌥+щелчок: группу)", + "Add in place": "Добавить на месте", + "Add in place: the library references the files where they are; nothing is copied or moved.": "Добавить на месте: библиотека ссылается на файлы там, где они лежат; ничего не копируется и не перемещается.", + "Add keyword": "Добавить ключевое слово", + "Add number": "Добавить номер", + "Add to": "Добавить в", + "Add to Album": "Добавить в альбом", + "Add to Favorites": "Добавить в избранное", + "Add to My Photos": "Добавить в «Мои фото»", + "Add to Selected Photos": "Добавить к выбранным фото", + "Add to Target Album": "Добавить в целевой альбом", + "Add to this mask": "Добавить к этой маске", + "Add/Subtract/Intersect Mask": "Добавить/вычесть/пересечь маску", + "Added to photos you import that don't already have it.": "Добавляется к импортируемым фото, у которых этого ещё нет.", + "Adding": "Добавление", + "Advanced search…": "Расширенный поиск…", + "After": "После", + "After 30 days": "Через 30 дней", + "After 7 days": "Через 7 дней", + "After 90 days": "Через 90 дней", + "After a year": "Через год", + "Album": "Альбом", + "Albums": "Альбомы", + "All": "Все", + "All Metadata": "Все метаданные", + "All Metadata…": "Все метаданные…", + "All Photos": "Все фото", + "All rights reserved": "Все права защищены", + "All types": "Все типы", + "Alt Text": "Альтернативный текст", + "Always": "Всегда", + "Amount": "Количество", + "Aperture": "Диафрагма", + "Application": "Приложение", + "Apply Color Label Set": "Применить набор цветовых меток", + "Apply Filter Preset": "Применить пресет фильтра", + "Apply Metadata Preset": "Применить пресет метаданных", + "Apply Point Curve Preset": "Применить пресет точечной кривой", + "Apply Preset": "Применить пресет", + "Apply Settings JSON": "Применить JSON настроек", + "Approved": "Одобрено", + "Aqua": "Голубой", + "Aqua Hue": "Тон голубого", + "Aqua Luminance": "Яркость голубого", + "Aqua Saturation": "Насыщенность голубого", + "ArtCraft Website": "Сайт ArtCraft", + "ArtCraft — more creative apps": "ArtCraft — другие творческие приложения", + "As Shot": "Как снято", + "As shot": "Как снято", + "Ascending": "По возрастанию", + "Aspect": "Пропорции", + "Aspect Ratio": "Соотношение сторон", + "Assisted Culling": "Автоматический отбор", + "Assisted Culling…": "Автоматический отбор…", + "Auto": "Авто", + "Auto Advance": "Автопереход", + "Auto Advance Off": "Автопереход выкл.", + "Auto Advance On": "Автопереход вкл.", + "Auto Advance after rating or flagging": "Автопереход после оценки или пометки флагом", + "Auto Advance: move to the next photo after rating or flagging": "Автопереход: переходить к следующему фото после оценки или пометки флагом", + "Auto Align": "Автовыравнивание", + "Auto B&W Mix": "Авто-микс Ч/Б", + "Auto Crop": "Автокадрирование", + "Auto Import": "Автоимпорт", + "Auto Import Now": "Выполнить автоимпорт сейчас", + "Auto Import Settings": "Настройки автоимпорта", + "Auto Mask": "Автомаска", + "Auto Settings": "Автонастройки", + "Auto Straighten": "Автовыравнивание горизонта", + "Auto Sync": "Автосинхронизация", + "Auto-Stack by Capture Time": "Автостопка по времени съёмки", + "Auto-Stack by Capture Time…": "Автостопка по времени съёмки…", + "Auto-Tag Photos from Tracklog": "Автопривязка фото по треку", + "Auto-Tag from Tracklog": "Автопривязка по треку", + "Auto-Tag from Tracklog…": "Автопривязка по треку…", + "Automatic": "Автоматически", + "Automatically Write Changes into XMP": "Автоматически записывать изменения в XMP", + "Automatically write changes into XMP sidecars": "Автоматически записывать изменения в XMP-файлы", + "Autumn Gold": "Осеннее золото", + "B&W": "Ч/Б", + "B&W Mix": "Микс Ч/Б", + "B&W Mixer": "Микшер Ч/Б", + "Back": "Назад", + "Back Up Library…": "Резервная копия библиотеки…", + "Back to Edit": "Вернуться к редактированию", + "Back to Edit (E)": "Вернуться к редактированию (E)", + "Back to Grid": "Вернуться к сетке", + "Background": "Фон", + "Balance": "Баланс", + "Basic": "Основные", + "Before": "До", + "Before/After (Y)": "До/после (Y)", + "Before/After Settings": "Настройки «до/после»", + "Before/After Split": "Разделение «до/после»", + "Before/After Split Top/Bottom": "Разделение «до/после» сверху/снизу", + "Before/After Top/Bottom": "До/после сверху/снизу", + "Begin Interaction": "Начать взаимодействие", + "Bit depth": "Разрядность", + "Black & White": "Чёрно-белое", + "Blacks": "Чёрные", + "Bleach Bypass": "Обход отбеливания", + "Bleached": "Выбеленный", + "Blending": "Наложение", + "Blue": "Синий", + "Blue Hour Boost": "Усиление синего часа", + "Blue Hue": "Тон синего", + "Blue Luminance": "Яркость синего", + "Blue Primary": "Основной синий", + "Blue Saturation": "Насыщенность синего", + "Blue channel": "Синий канал", + "Blue/Yellow Fringe": "Сине-жёлтая кайма", + "Blurry photos below the score are flagged as rejects.": "Размытые фото с оценкой ниже порога помечаются как отклонённые.", + "Boundary Warp": "Деформация границ", + "Bright & Airy": "Светлый и воздушный", + "Bring All to Front": "Показать все окна", + "Browse Folder": "Выбрать папку", + "Browse Folder…": "Выбрать папку…", + "Browse Profiles": "Обзор профилей", + "Browse…": "Обзор…", + "Brush": "Кисть", + "Brush Settings": "Настройки кисти", + "Brush Stroke": "Мазок кисти", + "Build Previews": "Создать просмотры", + "Build Smart Previews": "Создать смарт-просмотры", + "By Date": "По дате", + "By day (YYYY/YYYY-MM-DD)": "По дням (ГГГГ/ГГГГ-ММ-ДД)", + "By month (YYYY/YYYY-MM)": "По месяцам (ГГГГ/ГГГГ-ММ)", + "Calibration": "Калибровка", + "Camera": "Камера", + "Cameras and Cards": "Камеры и карты", + "Cancel": "Отмена", + "Cancel Download": "Отменить загрузку", + "Cancel Interaction": "Отменить взаимодействие", + "Cancel Preview Build": "Отменить создание просмотров", + "Candidate": "Кандидат", + "Caption": "Подпись", + "Capture Date": "Дата съёмки", + "Captured": "Снято", + "Catalog Statistics": "Статистика каталога", + "Check All": "Отметить все", + "Check Output Path": "Проверить путь вывода", + "Choose Another Library…": "Выбрать другую библиотеку…", + "Choose Edit Settings to Copy": "Выбрать настройки для копирования", + "Choose Edit Settings to Copy…": "Выбрать настройки для копирования…", + "Choose Folder…": "Выбрать папку…", + "Choose a photo in the grid or filmstrip": "Выберите фото в сетке или на ленте", + "Choose…": "Выбрать…", + "Cinematic": "Кинематографичный", + "City": "Город", + "Clarity": "Чёткость", + "Clean Studio": "Чистая студия", + "Clear": "Очистить", + "Clear Cache": "Очистить кэш", + "Clear Filters": "Сбросить фильтры", + "Clear Guides": "Убрать направляющие", + "Clear History": "Очистить историю", + "Clear Preview Cache": "Очистить кэш просмотров", + "Clear Quick Collection": "Очистить быструю коллекцию", + "Clear Reference": "Убрать эталон", + "Clear all": "Очистить всё", + "Clear form": "Очистить форму", + "Cleared History": "История очищена", + "Click Zoom Ratio": "Масштаб по щелчку", + "Click a tag to insert it. Examples are for IMG_0042.CR3 taken 14 Jan 2026, 05:58:48.": "Щёлкните по тегу, чтобы вставить его. Примеры приведены для IMG_0042.CR3, снятого 14 января 2026 в 05:58:48.", + "Click photos in the grid to toggle the keyword; Esc stops": "Щёлкайте по фото в сетке, чтобы переключать ключевое слово; Esc — остановить", + "Click the photo to pick a colour · ⇧-click adds more": "Щёлкните по фото, чтобы выбрать цвет · ⇧+щелчок добавляет ещё", + "Click the photo to pick a colour; ⇧-click adds more (up to 5)": "Щёлкните по фото, чтобы выбрать цвет; ⇧+щелчок добавляет ещё (до 5)", + "Clone": "Клонировать", + "Close": "Закрыть", + "Close Panel": "Закрыть панель", + "Close Window": "Закрыть окно", + "Close all": "Закрыть все", + "Close file": "Закрыть файл", + "Cloudy": "Облачно", + "Collapse All Stacks": "Свернуть все стопки", + "Color": "Цвет", + "Color Grading": "Цветокоррекция", + "Color Label Names": "Названия цветовых меток", + "Color Label Sets": "Наборы цветовых меток", + "Color Mixer": "Цветовой микшер", + "Color Noise Reduction": "Подавление цветового шума", + "Color Priority": "Приоритет цвета", + "Color Range": "Цветовой диапазон", + "Color space": "Цветовое пространство", + "Color space: sRGB (AVIF)": "Цветовое пространство: sRGB (AVIF)", + "Combine files…": "Объединить файлы…", + "Command Journal": "Журнал команд", + "Comments panel": "Панель комментариев", + "Compare": "Сравнение", + "Compare (Shift+C)": "Сравнение (Shift+C)", + "Compare Before and After": "Сравнить «до» и «после»", + "Compare needs at least two photos": "Для сравнения нужно минимум два фото", + "Component options": "Параметры компонента", + "Compression": "Сжатие", + "Concrete Cool": "Холодный бетон", + "Confirm before deleting": "Подтверждать удаление", + "Confirm before moving photos to Recently Deleted": "Подтверждать перемещение фото в «Недавно удалённые»", + "Constrain Crop": "Ограничить кадрирование", + "Continue Without Saving": "Продолжить без сохранения", + "Contrast": "Контрастность", + "Convert to DNG": "Преобразовать в DNG", + "Cool Fade": "Холодное выцветание", + "Cool Morning": "Холодное утро", + "Copy": "Копировать", + "Copy After's Settings to Before": "Скопировать настройки «после» в «до»", + "Copy Before's Settings to After": "Скопировать настройки «до» в «после»", + "Copy Edit Settings": "Копировать правки", + "Copy History Step Settings to Before": "Скопировать настройки шага истории в «до»", + "Copy Metadata": "Копировать метаданные", + "Copy as DNG": "Копировать как DNG", + "Copy into the library (else use the files where they are)": "Копировать в библиотеку (иначе файлы используются там, где лежат)", + "Copy pages": "Копировать страницы", + "Copy the files (into the library's Originals/, or a folder you choose)": "Копировать файлы (в папку «Originals/» библиотеки или в выбранную папку)", + "Copy to": "Копировать в", + "Copy to Clipboard": "Копировать в буфер обмена", + "Copy: the files are copied to the folder below and the library uses the copies; the originals are left as they are.": "Копирование: файлы копируются в указанную ниже папку, а библиотека использует копии; оригиналы остаются как есть.", + "Copyright": "Авторские права", + "Copyright Info URL": "URL сведений об авторских правах", + "Copyright Status": "Статус авторских прав", + "Country": "Страна", + "Create Album": "Создать альбом", + "Create Album…": "Создать альбом…", + "Create Folder": "Создать папку", + "Create Folder…": "Создать папку…", + "Create New Mask": "Создать новую маску", + "Create PDF from clipboard": "Создать PDF из буфера обмена", + "Create PDF from file…": "Создать PDF из файла…", + "Create PDF from images…": "Создать PDF из изображений…", + "Create Preset": "Создать пресет", + "Create Preset…": "Создать пресет…", + "Create Proof Copy": "Создать пробную копию", + "Create Smart Album": "Создать смарт-альбом", + "Create Smart Album from Filter…": "Создать смарт-альбом из фильтра…", + "Create Smart Album…": "Создать смарт-альбом…", + "Create Stack": "Создать стопку", + "Create Version": "Создать версию", + "Create Version from Step": "Создать версию из шага", + "Create Virtual Copy": "Создать виртуальную копию", + "Creator": "Автор", + "Crisp Landscape": "Чёткий пейзаж", + "Crisp Vista": "Чёткая панорама", + "Crop": "Кадрирование", + "Crop & Rotate": "Кадрирование и поворот", + "Crop & Rotate (C)": "Кадрирование и поворот (C)", + "Crop Aspect": "Пропорции кадра", + "Cross Process": "Кросс-процесс", + "Culling": "Отбор", + "Curve": "Кривая", + "Custom": "Свои", + "Custom…": "Свои…", + "Cut": "Вырезать", + "Cut pages": "Вырезать страницы", + "Cycle Crop Overlay": "Переключить сетку кадрирования", + "Cycle Crop Overlay Orientation": "Переключить ориентацию сетки кадрирования", + "Cycle Info Overlay": "Переключить информационную накладку", + "Cycle Mask Overlay Color": "Переключить цвет наложения маски", + "Cycle Mask Overlay Mode": "Переключить режим наложения маски", + "Cylindrical": "Цилиндрическая", + "Dark & Moody": "Тёмный и атмосферный", + "Darken": "Затемнить", + "Darks": "Тёмные тона", + "Date Groups": "Группы по датам", + "Day": "День", + "Daylight": "Дневной свет", + "Decrease Brush Feather": "Уменьшить растушёвку кисти", + "Decrease Brush Size": "Уменьшить размер кисти", + "Deep Sky": "Глубокое небо", + "Defringe": "Убрать кайму", + "Deghost Amount": "Степень удаления артефактов", + "Dehaze": "Убрать дымку", + "Delete": "Удалить", + "Delete (⌫)": "Удалить (⌫)", + "Delete Album": "Удалить альбом", + "Delete All Masks": "Удалить все маски", + "Delete Color Label Set": "Удалить набор цветовых меток", + "Delete Export Preset": "Удалить пресет экспорта", + "Delete Filter Preset": "Удалить пресет фильтра", + "Delete Imported Profile": "Удалить импортированный профиль", + "Delete Keyword": "Удалить ключевое слово", + "Delete Keyword Set": "Удалить набор ключевых слов", + "Delete Mask": "Удалить маску", + "Delete Metadata Preset": "Удалить пресет метаданных", + "Delete Permanently": "Удалить навсегда", + "Delete Photo": "Удалить фото", + "Delete Photos": "Удалить фото", + "Delete Point Color Sample": "Удалить точечный образец цвета", + "Delete Point Curve Preset": "Удалить пресет точечной кривой", + "Delete Preset": "Удалить пресет", + "Delete Red Eye Correction": "Удалить коррекцию красных глаз", + "Delete Spot": "Удалить пятно", + "Delete Version": "Удалить версию", + "Delete all": "Удалить все", + "Delete all spots": "Удалить все пятна", + "Delete mask": "Удалить маску", + "Delete them": "Удалить их", + "Density": "Плотность", + "Depth Range": "Диапазон глубины", + "Descending": "По убыванию", + "Describe…": "Описать…", + "Deselect": "Снять выделение", + "Deselect All": "Снять выделение со всех", + "Desert Heat": "Жар пустыни", + "Desert Warmth": "Тепло пустыни", + "Detail": "Детализация", + "Detail (D)": "Детализация (D)", + "Details": "Подробности", + "Diagonal": "По диагонали", + "Discard Preview Cache": "Удалить кэш просмотров", + "Display P3": "Display P3", + "Display theme": "Тема оформления", + "Distortion": "Искажение", + "Document properties…": "Свойства документа…", + "Don't enlarge": "Не увеличивать", + "Done": "Готово", + "Drag along the horizon; double-click for Auto": "Ведите вдоль горизонта; двойной щелчок — авто", + "Drag over an eye on the photo; the pupil inside is found automatically.": "Наведите на глаз на фото; зрачок внутри будет найден автоматически.", + "Draw Guides": "Нарисовать направляющие", + "Duplicate": "Дублировать", + "Duplicate Mask": "Дублировать маску", + "Duplicate and Invert Mask": "Дублировать и инвертировать маску", + "Each / starts a folder level; tags are filled in per photo (a level with missing metadata is \"unknown\").": "Каждый символ / начинает новый уровень папок; теги подставляются для каждого фото (уровень без метаданных называется «unknown»).", + "Edit": "Правка", + "Edit (E)": "Правка (E)", + "Edit Capture Time": "Изменить время съёмки", + "Edit Capture Time…": "Изменить время съёмки…", + "Edit Color Label Names": "Изменить названия цветовых меток", + "Edit Color Label Names…": "Изменить названия цветовых меток…", + "Edit Copy for External Editor": "Править копию во внешнем редакторе", + "Edit Info": "Править сведения", + "Edit Label Names…": "Изменить названия меток…", + "Edit Mask Component": "Изменить компонент маски", + "Edit Sections": "Изменить разделы", + "Edit Smart Album": "Изменить смарт-альбом", + "Edit Smart Album…": "Изменить смарт-альбом…", + "Edit Spot": "Изменить пятно", + "Edit in External Editor": "Править во внешнем редакторе", + "Edited": "Изменено", + "Effect": "Эффект", + "Effects": "Эффекты", + "Enable Lens Corrections": "Включить коррекцию объектива", + "End Interaction": "Завершить взаимодействие", + "English": "Английский", + "Enter Full Screen": "Полноэкранный режим", + "Erase": "Стереть", + "Every EXIF, GPS and XMP field in the file": "Все поля EXIF, GPS и XMP в файле", + "Existing previews": "Имеющиеся просмотры", + "Exit": "Выход", + "Expand All Stacks": "Развернуть все стопки", + "Expand/Collapse Stack": "Развернуть/свернуть стопку", + "Export": "Экспорт", + "Export Album with Preset": "Экспортировать альбом с пресетом", + "Export Album…": "Экспортировать альбом…", + "Export As…": "Экспортировать как…", + "Export Group…": "Экспортировать группу…", + "Export Now": "Экспортировать сейчас", + "Export Point Curve Presets": "Экспортировать пресеты точечной кривой", + "Export Point Curve Presets…": "Экспортировать пресеты точечной кривой…", + "Export Presets": "Экспортировать пресеты", + "Export Presets…": "Экспортировать пресеты…", + "Export User Presets…": "Экспортировать пользовательские пресеты…", + "Export with Preset": "Экспорт с пресетом", + "Export with Previous": "Экспорт с предыдущими настройками", + "Exporting": "Экспорт", + "Export…": "Экспорт…", + "Exposure": "Экспозиция", + "Exposure (shutter · aperture · ISO)": "Экспозиция (выдержка · диафрагма · ISO)", + "Extended Description": "Расширенное описание", + "External Editor": "Внешний редактор", + "Faded Matte": "Выцветший матовый", + "Faded Slide": "Выцветший слайд", + "Faded Urban": "Выцветший урбан", + "Favorite Preset": "Избранный пресет", + "Favorite Profile": "Избранный профиль", + "Favorites": "Избранное", + "Feather": "Растушёвка", + "File": "Файл", + "File & date": "Файл и дата", + "File Name": "Имя файла", + "File Path": "Путь к файлу", + "File Size": "Размер файла", + "File name": "Имя файла", + "Fill Edges": "Заполнить края", + "Film": "Плёнка", + "Filmstrip": "Лента", + "Filmstrip (/)": "Лента (/)", + "Filter": "Фильтр", + "Filter Bar": "Панель фильтров", + "Filter Presets": "Пресеты фильтров", + "Filter bar": "Панель фильтров", + "Filter fields": "Поля фильтра", + "Find Dust Spots": "Найти пылинки", + "Find Missing Photos": "Найти отсутствующие фото", + "Find Missing Photos…": "Найти отсутствующие фото…", + "Find Similar Photos": "Найти похожие фото", + "Find tools and commands…": "Найти инструменты и команды…", + "Find…": "Найти…", + "Fit": "Вписать", + "Fit (F)": "Вписать (F)", + "Fit on Screen": "Вписать в экран", + "Fit to width": "По ширине", + "Fit visible": "Вписать видимое", + "Flag": "Флаг", + "Flag as Pick": "Пометить как выбранное", + "Flag as Reject": "Пометить как отклонённое", + "Flash": "Вспышка", + "Flip H": "Отразить по гор.", + "Flip Horizontal": "Отразить по горизонтали", + "Flip Overlay (⇧O)": "Отразить наложение (⇧O)", + "Flip V": "Отразить по верт.", + "Flip Vertical": "Отразить по вертикали", + "Flow": "Поток", + "Fluorescent": "Флуоресцентная лампа", + "Focal length": "Фокусное расстояние", + "Folder": "Папка", + "Folder name (renamed on disk; its photos follow)": "Имя папки (переименовывается на диске; фото следуют за ней)", + "Folders": "Папки", + "Folders and {date} use the capture time; a photo without one uses today's date.": "Папки и {date} используют время съёмки; фото без него получает сегодняшнюю дату.", + "Food": "Еда", + "Forget Unchanged Local Photos": "Забыть неизменённые локальные фото", + "Forget unchanged photos": "Забыть неизменённые фото", + "Form fields panel": "Панель полей формы", + "Format": "Формат", + "Forward": "Вперёд", + "Free": "Свободно", + "Fresh & Bright": "Свежий и яркий", + "Friday": "Пятница", + "Full": "Полный", + "Full Screen Preview": "Полноэкранный просмотр", + "Full screen mode": "Полноэкранный режим", + "Full size": "Полный размер", + "GPS Track Log": "GPS-трек", + "GPU Rendering": "Рендеринг на GPU", + "General": "Общие", + "Geometry": "Геометрия", + "Get Develop Settings": "Получить настройки коррекции", + "Global Hue": "Общий тон", + "Global Luminance": "Общая яркость", + "Global Saturation": "Общая насыщенность", + "Glossy": "Глянцевый", + "Go to History Step": "Перейти к шагу истории", + "Golden": "Золотой", + "Golden Glow": "Золотое сияние", + "Golden Hour": "Золотой час", + "Grain": "Зерно", + "Graphic": "Графичный", + "Green": "Зелёный", + "Green Amount": "Количество зелёного", + "Green Hue": "Тон зелёного", + "Green Hue High": "Тон зелёного (верх)", + "Green Hue Low": "Тон зелёного (низ)", + "Green Luminance": "Яркость зелёного", + "Green Primary": "Основной зелёный", + "Green Saturation": "Насыщенность зелёного", + "Green channel": "Зелёный канал", + "Grid": "Сетка", + "Grid Info": "Информация сетки", + "Gritty Street": "Жёсткая улица", + "Group": "Группа", + "Group by Date: Automatic": "Группировка по дате: автоматически", + "Group by Day": "Группировать по дням", + "Group by Month": "Группировать по месяцам", + "Group by Year": "Группировать по годам", + "Group by date": "Группировать по дате", + "Group into Stack": "Сгруппировать в стопку", + "Guided": "Направляемый", + "Guided Upright": "Направляемое выравнивание", + "Guided Upright Guides": "Направляющие выравнивания", + "HDR Merge": "Слияние HDR", + "HDR Panorama Merge": "Слияние HDR-панорамы", + "HDR Panorama with Last Settings": "HDR-панорама с последними настройками", + "HDR Panorama…": "HDR-панорама…", + "HDR with Last Settings": "HDR с последними настройками", + "HDR…": "HDR…", + "Heal": "Лечение", + "Heal the small, soft dark spots sensor dust leaves on smooth areas": "Убирать мелкие мягкие тёмные пятна от пыли на матрице на ровных участках", + "Height": "Высота", + "Height (px)": "Высота (px)", + "Help": "Справка", + "Hide LightCraft": "Скрыть LightCraft", + "Hide Mask": "Скрыть маску", + "Hide Others": "Скрыть остальные", + "Hides this shortcut only; the folder and its photos stay as they are": "Скрывает только этот ярлык; папка и её фото остаются как есть", + "High": "Высокий", + "High Contrast B&W": "Контрастное Ч/Б", + "Highlight": "Свет", + "Highlights": "Света", + "Highlights Hue": "Тон светов", + "Highlights Luminance": "Яркость светов", + "Highlights Saturation": "Насыщенность светов", + "Highlights split": "Разделение светов", + "Histogram": "Гистограмма", + "History": "История", + "History & Activity (Y)": "История и активность (Y)", + "Horizontal": "По горизонтали", + "Horizontal text": "Горизонтальный текст", + "Hue": "Тон", + "Hue Range": "Диапазон тонов", + "If file exists": "Если файл существует", + "Image": "Изображение", + "Import": "Импорт", + "Import Date": "Дата импорта", + "Import Photos": "Импорт фото", + "Import Photos…": "Импорт фото…", + "Import Point Curve Presets": "Импортировать пресеты точечной кривой", + "Import Point Curve Presets…": "Импортировать пресеты точечной кривой…", + "Import Presets": "Импортировать пресеты", + "Import Presets…": "Импортировать пресеты…", + "Import Profiles": "Импортировать профили", + "Import Profiles & Presets…": "Импортировать профили и пресеты…", + "Import from Device": "Импорт с устройства", + "Import from Folder…": "Импорт из папки…", + "Import photos with File → Import Photos… (Cmd+Shift+I), or drop them here": "Импортируйте фото через Файл → Импорт фото… (Cmd+Shift+I) или перетащите их сюда", + "Imported Photos": "Импортированные фото", + "Importing": "Импорт", + "Importing scans the source for photos; it doesn't add the folder to Local (use Local → Browse Folder… to work in a folder without importing).": "При импорте источник сканируется на фото; папка не добавляется в «Локальное» (чтобы работать с папкой без импорта, используйте Локальное → Выбрать папку…).", + "In Recently Deleted": "В «Недавно удалённых»", + "In library": "В библиотеке", + "In memory — nothing is saved": "В памяти — ничего не сохраняется", + "In use": "Используется", + "In-memory session: these settings last until LightCraft quits.": "Сеанс в памяти: эти настройки действуют до закрытия LightCraft.", + "Include subfolders": "Включая вложенные папки", + "Increase Brush Feather": "Увеличить растушёвку кисти", + "Increase Brush Size": "Увеличить размер кисти", + "Info": "Информация", + "Info (I)": "Информация (I)", + "Info overlay": "Информационная накладка", + "Inspect Photo": "Просмотр фото", + "Instant ’70s": "Мгновенный стиль 70-х", + "Interface": "Интерфейс", + "Interface language": "Язык интерфейса", + "Intersect": "Пересечение", + "Intersect with": "Пересечь с", + "Into one folder": "В одну папку", + "Invert": "Инвертировать", + "Invert Mask": "Инвертировать маску", + "JPEG (Large)": "JPEG (большой)", + "JPEG (Small)": "JPEG (малый)", + "Join the ArtCraft Discord": "Присоединяйтесь к Discord ArtCraft", + "Join the ArtCraft Discord…": "Присоединиться к Discord ArtCraft…", + "Keep in Local": "Оставить в «Локальном»", + "Keep it on a drive with room: smart previews are about 1 MB per photo. The thumbnail cache stays in the library.": "Храните их на диске со свободным местом: смарт-просмотр занимает около 1 МБ на фото. Кэш миниатюр остаётся в библиотеке.", + "Keyboard Shortcuts": "Сочетания клавиш", + "Keyboard shortcuts": "Сочетания клавиш", + "Keyword": "Ключевое слово", + "Keyword Painter": "Кисть ключевых слов", + "Keyword Set": "Набор ключевых слов", + "Keyword Sets": "Наборы ключевых слов", + "Keyword Suggestions": "Подсказки ключевых слов", + "Keyword to paint": "Ключевое слово для нанесения", + "Keywords": "Ключевые слова", + "Keywords (K)": "Ключевые слова (K)", + "Keywords you add appear here; ⌥1–⌥9 apply them.": "Добавленные вами ключевые слова появятся здесь; ⌥1–⌥9 применяют их.", + "Landscape": "Пейзаж", + "Language": "Язык", + "Largest long edge the Detail view renders at; larger is sharper on big displays but slower.": "Максимальная длинная сторона, с которой рисуется режим «Детализация»; больше — чётче на больших экранах, но медленнее.", + "Last": "Последний", + "Last view": "Последний вид", + "Lateral Chromatic Aberration": "Боковая хроматическая аберрация", + "Layer": "Слой", + "Layers": "Слои", + "Leave them": "Оставить их", + "Lens": "Объектив", + "Lens Corrections": "Коррекция объектива", + "Level": "Уровень", + "Library": "Библиотека", + "Library Info": "Сведения о библиотеке", + "Library Originals": "Оригиналы библиотеки", + "Library Preferences": "Настройки библиотеки", + "Library State": "Состояние библиотеки", + "Licence:": "Лицензия:", + "Light": "Светлый", + "LightCraft Default": "По умолчанию в LightCraft", + "LightCraft Help": "Справка LightCraft", + "LightCraft Preset": "Пресет LightCraft", + "LightCraft Presets": "Пресеты LightCraft", + "LightCraft Website": "Сайт LightCraft", + "LightCraft couldn't find where to keep your library.": "LightCraft не удалось найти место для хранения вашей библиотеки.", + "LightCraft couldn't open the library at": "LightCraft не удалось открыть библиотеку в", + "LightCraft on GitHub": "LightCraft на GitHub", + "LightCraft website": "Сайт LightCraft", + "LightCraft — Second Window": "LightCraft — второе окно", + "Lights": "Светлые тона", + "Limit file size (KB, 0 = off)": "Ограничить размер файла (КБ, 0 = выкл.)", + "Linear Gradient": "Линейный градиент", + "List Albums": "Список альбомов", + "List Develop Controls": "Список элементов управления коррекцией", + "List History": "Список истории", + "List Presets": "Список пресетов", + "List Profiles": "Список профилей", + "List this folder in Local from now on": "Показывать эту папку в «Локальном» и дальше", + "Local": "Локальное", + "Local folders": "Локальные папки", + "Local library — no cloud account needed": "Локальная библиотека — учётная запись в облаке не нужна", + "Locate Missing File…": "Указать отсутствующий файл…", + "Locate Photo": "Найти фото", + "Location": "Местоположение", + "Long Edge": "Длинная сторона", + "Looking for photos…": "Поиск фото…", + "Lossless": "Без потерь", + "Low": "Низкий", + "Luminance": "Яркость", + "Luminance Range": "Диапазон яркости", + "Lush Greens": "Сочная зелень", + "Magenta": "Пурпурный", + "Magenta Hue": "Тон пурпурного", + "Magenta Luminance": "Яркость пурпурного", + "Magenta Saturation": "Насыщенность пурпурного", + "Make Candidate the Select": "Сделать кандидата выбранным", + "Manual": "Вручную", + "Marquee zoom": "Масштаб рамкой", + "Masking": "Маски", + "Masking (M)": "Маски (M)", + "Masks": "Маски", + "Match": "Совпадение", + "Match Total Exposures": "Совпадение общей экспозиции", + "Matte": "Матовый", + "Matte Soft": "Мягкий матовый", + "Med": "Средний", + "Medium Contrast": "Средний контраст", + "Megapixels": "Мегапиксели", + "Memory Budget": "Лимит памяти", + "Memory Usage": "Использование памяти", + "Memory for caches": "Память для кэшей", + "Menu": "Меню", + "Merge": "Слияние", + "Merge Keywords": "Объединить ключевые слова", + "Merge into…": "Объединить с…", + "Merging…": "Слияние…", + "Meta's terms, not LightCraft's. Downloading it means accepting them.": "Условия Meta, а не LightCraft. Загрузка означает их принятие.", + "Metadata": "Метаданные", + "Metadata Preset": "Пресет метаданных", + "Metadata Presets": "Пресеты метаданных", + "Metadata preset": "Пресет метаданных", + "Midpoint": "Средняя точка", + "Midtones": "Средние тона", + "Midtones Hue": "Тон средних тонов", + "Midtones Luminance": "Яркость средних тонов", + "Midtones Saturation": "Насыщенность средних тонов", + "Midtones split": "Разделение средних тонов", + "Minimize": "Свернуть", + "Missing Photos": "Отсутствующие фото", + "Misty Morning": "Туманное утро", + "Mode": "Режим", + "Modified Date": "Дата изменения", + "Monday": "Понедельник", + "Mono Red Filter": "Моно с красным фильтром", + "Mono Rich": "Насыщенное моно", + "Mono Soft": "Мягкое моно", + "Monochrome": "Монохром", + "Month": "Месяц", + "Moody": "Атмосферный", + "Moody Portrait": "Атмосферный портрет", + "More": "Ещё", + "More preset options": "Другие параметры пресета", + "Move": "Переместить", + "Move Album": "Переместить альбом", + "Move Down in Stack": "Опустить в стопке", + "Move Folder": "Переместить папку", + "Move Folder To…": "Переместить папку в…", + "Move Mask": "Переместить маску", + "Move Preset to Group": "Переместить пресет в группу", + "Move Up in Stack": "Поднять в стопке", + "Move the files (into the library's Originals/, or a folder you choose), removing them from the source": "Переместить файлы (в папку «Originals/» библиотеки или в выбранную папку), удалив их из источника", + "Move them": "Переместить их", + "Move to": "Переместить в", + "Move to Group": "Переместить в группу", + "Move: the files are moved to the folder below; each original (and its XMP sidecar) is removed from the source only after its copy is verified. Duplicates and files that fail stay where they are.": "Перемещение: файлы перемещаются в указанную ниже папку; каждый оригинал (и его XMP-файл) удаляется из источника только после проверки копии. Дубликаты и файлы с ошибками остаются на месте.", + "Muted": "Приглушённый", + "Muted Film": "Приглушённая плёнка", + "My Photos": "Мои фото", + "My Photos Panel": "Панель «Мои фото»", + "Name": "Название", + "Named": "Именованные", + "Names appear in the label menu, the filter bar and the Info panel, and are written to XMP. Empty = the colour's name.": "Названия показываются в меню меток, на панели фильтров и в панели «Информация» и записываются в XMP. Пусто = название цвета.", + "Navigator": "Навигатор", + "Needs Work": "Требует доработки", + "Neon Dusk": "Неоновые сумерки", + "Neon Night": "Неоновая ночь", + "Neutral": "Нейтральный", + "Never": "Никогда", + "New": "Новый", + "New Album": "Новый альбом", + "New Album…": "Новый альбом…", + "New Folder…": "Новая папка…", + "New Group…": "Новая группа…", + "New Smart Album": "Новый смарт-альбом", + "New Smart Album from Filter…": "Новый смарт-альбом из фильтра…", + "New Smart Album…": "Новый смарт-альбом…", + "New album": "Новый альбом", + "New album…": "Новый альбом…", + "New blank PDF": "Новый пустой PDF", + "New name": "Новое имя", + "New…": "Новый…", + "Next Photo": "Следующее фото", + "Night Blue": "Ночной синий", + "No": "Нет", + "No Camera or Card Found": "Камера или карта не найдены", + "No Date Groups": "Без групп по датам", + "No Stars": "Без звёзд", + "No Tool": "Нет инструмента", + "No automatic versions.": "Автоматических версий нет.", + "No camera": "Нет камеры", + "No date": "Нет даты", + "No dust spots found": "Пылинки не найдены", + "No edits yet.": "Правок пока нет.", + "No exposure information": "Нет данных об экспозиции", + "No filter presets yet": "Пресетов фильтров пока нет", + "No lens data embedded in this file (DNG lens opcodes only).": "В этом файле нет данных об объективе (только опкоды объектива DNG).", + "No masks yet. Choose a mask type above.": "Масок пока нет. Выберите тип маски выше.", + "No matching photos": "Нет подходящих фото", + "No metadata found": "Метаданные не найдены", + "No new presets": "Новых пресетов нет", + "No photo selected": "Фото не выбрано", + "No photos": "Нет фото", + "No photos found": "Фото не найдены", + "No photos in this folder": "В этой папке нет фото", + "No photos match the active filters": "Нет фото, соответствующих активным фильтрам", + "No photos match the filters (View → Clear Filters)": "Нет фото, соответствующих фильтрам (Вид → Сбросить фильтры)", + "No raw photos yet: cameras appear here once their photos are in the library.": "RAW-фото пока нет: камеры появятся здесь, когда их фото попадут в библиотеку.", + "No reference photo": "Нет эталонного фото", + "No usable GPU found: rendering on the CPU": "Подходящий GPU не найден: рендеринг на CPU", + "No versions yet. Create one to keep this look.": "Версий пока нет. Создайте версию, чтобы сохранить этот вид.", + "Noise": "Шум", + "Noise Reduction": "Подавление шума", + "Non-raw photos": "Не RAW-фото", + "None": "Нет", + "Nothing to compare": "Нечего сравнивать", + "Nothing to survey": "Нечего просматривать в обзоре", + "Nudge Develop Value": "Подправить значение коррекции", + "OK": "OK", + "Object": "Объект", + "Off": "Выкл.", + "Offset X": "Смещение по X", + "Offset Y": "Смещение по Y", + "Opacity": "Непрозрачность", + "Opacity (%)": "Непрозрачность (%)", + "Open Library": "Открыть библиотеку", + "Open Library…": "Открыть библиотеку…", + "Open in": "Открыть в", + "Open in Detail": "Открыть в режиме «Детализация»", + "Open the place in OpenStreetMap": "Открыть место в OpenStreetMap", + "Open…": "Открыть…", + "Optics": "Оптика", + "Optimize Library": "Оптимизировать библиотеку", + "Optional name": "Название (необязательно)", + "Options": "Параметры", + "Orange": "Оранжевый", + "Orange Hue": "Тон оранжевого", + "Orange Luminance": "Яркость оранжевого", + "Orange Saturation": "Насыщенность оранжевого", + "Orientation": "Ориентация", + "Original": "Оригинал", + "Original + Settings": "Оригинал + настройки", + "Original files": "Оригиналы файлов", + "Original offline · editing the smart preview": "Оригинал недоступен · правка смарт-просмотра", + "Original offline · no smart preview": "Оригинал недоступен · нет смарт-просмотра", + "Originals": "Оригиналы", + "Other images (JPEG, PNG, TIFF, HEIC…)": "Другие изображения (JPEG, PNG, TIFF, HEIC…)", + "Overlay": "Наложение", + "Overwrite": "Перезаписать", + "Pages": "Страницы", + "Paint": "Закрасить", + "Paint only areas like the one under the brush": "Закрашивать только области, похожие на ту, что под кистью", + "Paint over a distraction on the photo to remove it.": "Закрасьте лишнее на фото, чтобы убрать это.", + "Paint overlay": "Наложение закраски", + "Panorama Merge": "Слияние панорамы", + "Panorama with Last Settings": "Панорама с последними настройками", + "Panorama…": "Панорама…", + "Parametric curve": "Параметрическая кривая", + "Paste": "Вставить", + "Paste Edit Settings": "Вставить настройки правки", + "Paste Metadata": "Вставить метаданные", + "Paste Selected Settings": "Вставить выбранные настройки", + "Paste Selected Settings…": "Вставить выбранные настройки…", + "Paste Settings from Previous": "Вставить настройки из предыдущего", + "Paste pages": "Вставить страницы", + "Pastel Haze": "Пастельная дымка", + "Percent": "Проценты", + "Percentage": "Процент", + "Performance": "Производительность", + "Perspective": "Перспектива", + "Pet Eye": "Глаза питомца", + "Pet Eye Catchlight": "Блик в глазах питомца", + "Photo": "Фото", + "Photo Grid": "Сетка фото", + "Photo Grid (G)": "Сетка фото (G)", + "Photo Merge": "Слияние фото", + "Photo ▸ Edit in External Editor (⇧⌘E) renders a 16-bit TIFF copy, stacks it with the original and opens it here (an app name on macOS, a program path elsewhere).": "Фото → Править во внешнем редакторе (⇧⌘E) создаёт 16-битную TIFF-копию, помещает её в стопку с оригиналом и открывает здесь (имя приложения в macOS, путь к программе в других системах).", + "Photos": "Фото", + "Photos seen in Local but never added or changed leave the catalog when their folder hasn't been browsed for this long (checked when the library opens). Files stay on disk; browsing the folder shows them again.": "Фото, которые видели в «Локальном», но ни разу не добавляли и не меняли, исключаются из каталога, если папку не открывали так долго (проверяется при открытии библиотеки). Файлы остаются на диске; при просмотре папки они появятся снова.", + "Photos that arrive in this folder (tethering, a scanner, a sync app) are added as soon as they're complete.": "Фото, поступающие в эту папку (тетеринг, сканер, приложение синхронизации), добавляются, как только будут готовы.", + "Pick": "Выбрать", + "Pick a colour on the photo with the eyedropper.": "Выберите цвет на фото пипеткой.", + "Pick the sharpest photo of each burst": "Выбирать самое чёткое фото в каждой серии", + "Picked": "Выбрано", + "Picks": "Выбранные", + "Pixels": "Пиксели", + "Point Color": "Точечный цвет", + "Point Curve": "Точечная кривая", + "Point Curve Presets": "Пресеты точечной кривой", + "Point curve": "Точечная кривая", + "Portrait": "Портрет", + "Position": "Положение", + "Preferences": "Настройки", + "Preferences…": "Настройки…", + "Preset": "Пресет", + "Preset name": "Название пресета", + "Presets": "Пресеты", + "Presets & Profiles": "Пресеты и профили", + "Presets (Shift+P)": "Пресеты (Shift+P)", + "Presets ▾": "Пресеты ⌄", + "Preview": "Просмотр", + "Preview Build Progress": "Ход создания просмотров", + "Preview only": "Только просмотр", + "Preview size": "Размер просмотра", + "Previews": "Просмотры", + "Previous Photo": "Предыдущее фото", + "Print": "Печать", + "Print…": "Печать…", + "Profile": "Профиль", + "Profile Browser": "Обозреватель профилей", + "Profile Distortion": "Дисторсия профиля", + "Profile Menu": "Меню профилей", + "Profile Vignetting": "Виньетирование профиля", + "Profile:": "Профиль:", + "Profiles": "Профили", + "Projection": "Проекция", + "Properties": "Свойства", + "Public domain": "Общественное достояние", + "Pupil Size": "Размер зрачка", + "Purple": "Фиолетовый", + "Purple Amount": "Количество фиолетового", + "Purple Hue": "Тон фиолетового", + "Purple Hue High": "Тон фиолетового (верх)", + "Purple Hue Low": "Тон фиолетового (низ)", + "Purple Luminance": "Яркость фиолетового", + "Purple Saturation": "Насыщенность фиолетового", + "Put the locations you removed from Local back (no files change)": "Вернуть в «Локальное» удалённые вами места (файлы не меняются)", + "Quality": "Качество", + "Query Photos": "Запросить фото", + "Quick Collection": "Быстрая коллекция", + "Quick Develop": "Быстрая коррекция", + "Quiet Green": "Тихий зелёный", + "Quit": "Выход", + "Quit LightCraft": "Выйти из LightCraft", + "Radial Gradient": "Радиальный градиент", + "Radius": "Радиус", + "Random": "Случайно", + "Range": "Диапазон", + "Rating": "Оценка", + "Ratings & flags": "Оценки и флаги", + "Raw default": "Стандарт RAW", + "Raw defaults": "Стандарты RAW", + "Raw files": "RAW-файлы", + "Raw photos": "RAW-фото", + "Raw photos as DNG, with the edits embedded. Size, color and output options don't apply.": "RAW-фото как DNG со встроенными правками. Параметры размера, цвета и вывода не применяются.", + "Read Metadata from File": "Прочитать метаданные из файла", + "Read mode": "Режим чтения", + "Read the SAM License": "Прочитать лицензию SAM", + "Reading": "Чтение", + "Reading folder…": "Чтение папки…", + "Recently Added": "Недавно добавленные", + "Recently Deleted": "Недавно удалённые", + "Red": "Красный", + "Red Eye": "Красные глаза", + "Red Hue": "Тон красного", + "Red Luminance": "Яркость красного", + "Red Primary": "Основной красный", + "Red Saturation": "Насыщенность красного", + "Red channel": "Красный канал", + "Red/Cyan Fringe": "Красно-голубая кайма", + "Redo": "Повторить", + "Reference": "Эталон", + "Reference View": "Режим эталона", + "Reference the files where they are": "Ссылаться на файлы там, где они лежат", + "Refine Mask Edges": "Уточнить края маски", + "Refresh Source": "Обновить источник", + "Refresh Source (/)": "Обновить источник (/)", + "Reject": "Отклонить", + "Reject below focus": "Отклонять при недостаточной резкости", + "Rejected": "Отклонено", + "Rejects": "Отклонённые", + "Reload from Disk": "Перезагрузить с диска", + "Remove": "Удалить", + "Remove (H)": "Удалить (H)", + "Remove CA": "Убрать ХА", + "Remove Chromatic Aberration": "Убрать хроматические аберрации", + "Remove from Album": "Удалить из альбома", + "Remove from Favorites": "Удалить из избранного", + "Remove from Local": "Убрать из «Локального»", + "Remove from Photo": "Убрать с фото", + "Remove from Selected Photos": "Убрать с выбранных фото", + "Remove from Stack": "Убрать из стопки", + "Remove from survey": "Убрать из обзора", + "Remove location info": "Удалить сведения о местоположении", + "Remove this filter": "Убрать этот фильтр", + "Remove this group": "Убрать эту группу", + "Remove this rule": "Убрать это условие", + "Rename": "Переименовать", + "Rename Album": "Переименовать альбом", + "Rename Folder": "Переименовать папку", + "Rename Folder…": "Переименовать папку…", + "Rename Keyword": "Переименовать ключевое слово", + "Rename Keyword…": "Переименовать ключевое слово…", + "Rename Mask": "Переименовать маску", + "Rename Photos": "Переименовать фото", + "Rename Photos…": "Переименовать фото…", + "Rename Preset": "Переименовать пресет", + "Rename Preview": "Предпросмотр переименования", + "Rename Template Tags": "Теги шаблона переименования", + "Rename Version": "Переименовать версию", + "Rename…": "Переименовать…", + "Rendering": "Рендеринг", + "Rendering…": "Рендеринг…", + "Reset": "Сбросить", + "Reset All Curves": "Сбросить все кривые", + "Reset Before to Import State": "Сбросить «до» к состоянию при импорте", + "Reset Control": "Сбросить элемент управления", + "Reset Crop": "Сбросить кадрирование", + "Reset Edits": "Сбросить правки", + "Reset Parametric Curve": "Сбросить параметрическую кривую", + "Reset Section": "Сбросить раздел", + "Reset Tone Curve": "Сбросить тоновую кривую", + "Reset all curves: point curves (all channels) and parametric (double-click a channel to reset only that one)": "Сбросить все кривые: точечные (все каналы) и параметрическую (двойной щелчок по каналу сбрасывает только его)", + "Reset all edits (Cmd+Shift+R)": "Сбросить все правки (Cmd+Shift+R)", + "Reshuffle": "Перемешать", + "Resize to": "Изменить размер до", + "Resolution (ppi)": "Разрешение (ppi)", + "Restore": "Восстановить", + "Restore Library from Backup…": "Восстановить библиотеку из резервной копии…", + "Restore Version": "Восстановить версию", + "Retouch": "Ретушь", + "Revert": "Вернуть", + "Review Import": "Проверить импорт", + "Right-click a photo ▸ Set as Reference Photo, then View → Reference View (Shift+R)": "Щёлкните фото правой кнопкой → Назначить эталонным фото, затем Вид → Режим эталона (Shift+R)", + "Right-click a preset to delete it": "Щёлкните пресет правой кнопкой, чтобы удалить его", + "Rights Usage Terms": "Условия использования прав", + "Rotate": "Повернуть", + "Rotate Crop Aspect": "Повернуть пропорции кадра", + "Rotate Left": "Повернуть влево", + "Rotate Right": "Повернуть вправо", + "Roughness": "Шероховатость", + "Roundness": "Округлость", + "SAM 3 download stopped: it resumes where it left off next time.": "Загрузка SAM 3 остановлена: в следующий раз она продолжится с того же места.", + "Same as raw default": "Как стандарт RAW", + "Sample Mask Color": "Взять образец цвета маски", + "Sample a colour on the photo": "Взять образец цвета на фото", + "Saturation": "Насыщенность", + "Saturation Range": "Диапазон насыщенности", + "Saturday": "Суббота", + "Save": "Сохранить", + "Save As…": "Сохранить как…", + "Save Color Label Set": "Сохранить набор цветовых меток", + "Save Current Filter as Preset…": "Сохранить текущий фильтр как пресет…", + "Save Current Keywords as Set…": "Сохранить текущие ключевые слова как набор…", + "Save Export Preset": "Сохранить пресет экспорта", + "Save Filter Preset": "Сохранить пресет фильтра", + "Save Keyword Set": "Сохранить набор ключевых слов", + "Save Metadata Preset": "Сохранить пресет метаданных", + "Save Metadata Preset…": "Сохранить пресет метаданных…", + "Save Metadata to File": "Сохранить метаданные в файл", + "Save Point Curve Preset": "Сохранить пресет точечной кривой", + "Save Point Curve…": "Сохранить точечную кривую…", + "Save as Preset": "Сохранить как пресет", + "Save as Smart Album…": "Сохранить как смарт-альбом…", + "Save as set": "Сохранить как набор", + "Save as…": "Сохранить как…", + "Save preset": "Сохранить пресет", + "Scale": "Масштаб", + "Scanning": "Сканирование", + "Scores stay on the photos: filter or make smart albums with Focus and Best of Similar Shots.": "Оценки остаются на фото: фильтруйте или создавайте смарт-альбомы по Focus и Best of Similar Shots.", + "Screen": "Экран", + "Search Photos": "Поиск фото", + "Search…": "Поиск…", + "Seasons": "Времена года", + "Second Window": "Второе окно", + "Select": "Выбрать", + "Select All": "Выбрать все", + "Select Luminance Range": "Выбрать диапазон яркости", + "Select Mask": "Выбрать маску", + "Select Photos": "Выбрать фото", + "Select Spot": "Выбрать пятно", + "Select a photo to edit": "Выберите фото для правки", + "Select by": "Выбрать по", + "Select by Flag, Rating or Label": "Выбрать по флагу, оценке или метке", + "Select photos, then choose View → Survey (N)": "Выберите фото, затем Вид → Обзор (N)", + "Select two photos, then choose View → Compare (Shift+C)": "Выберите два фото, затем Вид → Сравнение (Shift+C)", + "Selected": "Выделено", + "Selenium Tone": "Селеновое тонирование", + "Send Feedback…": "Отправить отзыв…", + "Sepia Tone": "Сепия", + "Services": "Службы", + "Set Before": "Назначить «до»", + "Set Color Label": "Назначить цветовую метку", + "Set Crop": "Задать кадрирование", + "Set Develop Values": "Задать значения коррекции", + "Set Flag": "Задать флаг", + "Set Mask Adjustments": "Задать настройки маски", + "Set Point Curve": "Задать точечную кривую", + "Set Profile": "Задать профиль", + "Set Rating": "Задать оценку", + "Set as Album Cover": "Сделать обложкой альбома", + "Set as Before": "Назначить как «до»", + "Set as Reference Photo": "Назначить эталонным фото", + "Set as Target Album": "Назначить целевым альбомом", + "Set as Target Album (B adds to it)": "Назначить целевым альбомом (B добавляет в него)", + "Set as Top of Stack": "Сделать верхним в стопке", + "Set the mix from the photo's colours": "Задать микс по цветам фото", + "Settings": "Настройки", + "Settings new raw photos start from. Changing them doesn't touch photos already in the library.": "Настройки, с которых начинаются новые RAW-фото. Их изменение не затрагивает фото, уже находящиеся в библиотеке.", + "Settings to include": "Включаемые настройки", + "Settings…": "Настройки…", + "Shade": "Тень", + "Shadow": "Тень", + "Shadows": "Тени", + "Shadows Hue": "Тон теней", + "Shadows Luminance": "Яркость теней", + "Shadows Saturation": "Насыщенность теней", + "Shadows Tint": "Оттенок теней", + "Shadows split": "Разделение теней", + "Sharpen": "Повысить резкость", + "Sharpening": "Резкость", + "Sharpness": "Резкость", + "Short Edge": "Короткая сторона", + "Show All": "Показать все", + "Show Clipping": "Показать отсечение", + "Show Deghost Overlay": "Показать наложение удаления артефактов", + "Show Hidden Local Locations": "Показать скрытые места в «Локальном»", + "Show Luminance Map": "Показать карту яркости", + "Show Mask": "Показать маску", + "Show Mask Overlay": "Показать наложение маски", + "Show Mask Pins": "Показать булавки масок", + "Show Original": "Показать оригинал", + "Show Photo Counts": "Показывать количество фото", + "Show Photos with Keyword": "Показать фото с ключевым словом", + "Show Pins": "Показать булавки", + "Show Source": "Показать источник", + "Show Thumbnails": "Показывать миниатюры", + "Show destination gamut warning": "Показывать предупреждение о выходе за гамму устройства вывода", + "Show display gamut warning": "Показывать предупреждение о выходе за гамму дисплея", + "Show file names": "Показывать имена файлов", + "Show in Finder": "Показать в Finder", + "Show on Map": "Показать на карте", + "Show overlay (O)": "Показать наложение (O)", + "Show ratings, flags and edit badges": "Показывать оценки, флаги и значки правок", + "Show the Navigator while zoomed in": "Показывать навигатор при увеличении", + "Show the template tags; click one to insert it": "Показать теги шаблона; щёлкните по тегу, чтобы вставить его", + "Show/Hide Mask": "Показать/скрыть маску", + "Show/hide My Photos panel": "Показать/скрыть панель «Мои фото»", + "Shutter speed": "Выдержка", + "Side panels": "Боковые панели", + "Sidecar names": "Имена XMP-файлов", + "Size": "Размер", + "Size (% of short edge)": "Размер (% от короткой стороны)", + "Size limit": "Ограничение размера", + "Skip": "Пропустить", + "Sky": "Небо", + "Slideshow": "Слайд-шоу", + "Smart Album": "Смарт-альбом", + "Smart Album Rule Fields": "Поля условий смарт-альбома", + "Smart Preview Status": "Состояние смарт-просмотров", + "Smart Previews Location": "Расположение смарт-просмотров", + "Smart previews": "Смарт-просмотры", + "Smoothness": "Плавность", + "Soft B&W": "Мягкое Ч/Б", + "Soft Pastel": "Мягкий пастельный", + "Soft Proofing": "Экранная цветопроба", + "Soft Skin": "Мягкая кожа", + "Sort": "Сортировка", + "Sort by": "Сортировать по", + "Source": "Источник", + "Source code on GitHub": "Исходный код на GitHub", + "Spherical": "Сферическая", + "Spiral": "Спираль", + "Split Stack": "Разделить стопку", + "Spring Fresh": "Весенняя свежесть", + "Square Grid": "Квадратная сетка", + "Square Grid (G toggles)": "Квадратная сетка (G переключает)", + "Square Grid: show file names and formats": "Квадратная сетка: показывать имена и форматы файлов", + "Stack": "Стопка", + "Stack photos taken within this time of each other:": "Объединять в стопку фото, снятые с интервалом не более:", + "Stacks": "Стопки", + "Standard": "Стандартный", + "Start at": "Начать с", + "Start number": "Начальный номер", + "Startup": "Запуск", + "State / Province": "Область / регион", + "Stop": "Остановить", + "Stop Building Previews": "Остановить создание просмотров", + "Stop Using as Target Album": "Больше не использовать как целевой альбом", + "Stopping…": "Остановка…", + "Straighten": "Выровнять", + "Straighten Tool": "Инструмент «Выпрямление»", + "Strong Contrast": "Сильный контраст", + "Style": "Стиль", + "Subfolder": "Подпапка", + "Subfolder name": "Название подпапки", + "Subject": "Главный объект", + "Subtract": "Вычесть", + "Subtract from this mask": "Вычесть из этой маски", + "Suggestions": "Подсказки", + "Summer Haze": "Летняя дымка", + "Sunday": "Воскресенье", + "Survey": "Обзор", + "Survey (N)": "Обзор (N)", + "Swap": "Поменять", + "Swap Aspect (X)": "Поменять пропорции (X)", + "Swap Before and After Settings": "Поменять местами настройки «до» и «после»", + "Swap Compare Photos": "Поменять местами фото для сравнения", + "Switch light / dark theme": "Переключить светлую/тёмную тему", + "Sync Settings": "Синхронизировать настройки", + "System Info": "Сведения о системе", + "System Info…": "Сведения о системе…", + "System default": "Как в системе", + "Tags": "Теги", + "Tags: see Tags beside the template. Files are renamed on disk (with their XMP sidecars); existing names get -1, -2…": "Теги: см. «Теги» рядом с шаблоном. Файлы переименовываются на диске (вместе с XMP-файлами); к существующим именам добавляется -1, -2…", + "Take a snapshot": "Сделать снимок", + "Targeted Adjustment": "Целевая коррекция", + "Targeted adjustment: drag up/down on the photo to adjust the colours there": "Целевая коррекция: перетаскивайте вверх/вниз по фото, чтобы менять цвета в этом месте", + "Targeted adjustment: drag up/down on the photo to adjust the curve there": "Целевая коррекция: перетаскивайте вверх/вниз по фото, чтобы менять кривую в этом месте", + "Teal & Amber": "Бирюзовый и янтарный", + "Teal & Orange": "Бирюзовый и оранжевый", + "Temp": "Темп.", + "Temperature": "Температура", + "Template": "Шаблон", + "Temporary session — your library isn't open, and nothing you do here is saved.": "Временный сеанс — ваша библиотека не открыта, и ничего из сделанного здесь не сохраняется.", + "Text": "Текст", + "Text direction": "Направление текста", + "Texture": "Текстура", + "The SAM 3 model is installed: Object and Describe masks are ready.": "Модель SAM 3 установлена: маски «Объект» и «Описать» готовы.", + "The download didn't work:": "Загрузка не удалась:", + "The original files, unchanged, each with an XMP sidecar holding its edits.": "Исходные файлы без изменений, у каждого — XMP-файл с его правками.", + "The photo has these settings": "У фото есть эти настройки", + "Theme": "Тема", + "They can be restored from Recently Deleted until it is emptied.": "Их можно восстановить из «Недавно удалённых», пока этот раздел не очищен.", + "Thirds": "Правило третей", + "This folder is not available (drive disconnected?). Smart previews are not built or used until it is back or you choose another folder.": "Эта папка недоступна (диск отключён?). Смарт-просмотры не создаются и не используются, пока она не появится снова или вы не выберете другую папку.", + "This photo can't be opened": "Это фото не удаётся открыть", + "Threshold": "Порог", + "Thumbnail cache": "Кэш миниатюр", + "Thursday": "Четверг", + "Tint": "Оттенок", + "Title": "Заголовок", + "Toggle Keyword from Set": "Переключить ключевое слово из набора", + "Toggle Section": "Свернуть/развернуть раздел", + "Toggle Zoom": "Переключить масштаб", + "Tone Curve": "Тоновая кривая", + "Tools": "Инструменты", + "Top Level": "Верхний уровень", + "Transfer": "Перенос", + "Treatment": "Обработка", + "Triangle": "Треугольник", + "Try Again": "Повторить попытку", + "Tuesday": "Вторник", + "Tungsten": "Лампа накаливания", + "Turn Off": "Выключить", + "Turn on Include subfolders, or pick another folder under Local": "Включите «Включая вложенные папки» или выберите другую папку в «Локальном»", + "Type": "Тип", + "Uncheck All": "Снять все отметки", + "Undo": "Отменить", + "Unedited": "Без правок", + "Unflag": "Снять флаг", + "Unflagged": "Без флага", + "Ungroup Stack": "Разгруппировать стопку", + "Unknown Date": "Дата неизвестна", + "Unrated": "Без оценки", + "Unreadable": "Не читается", + "Update": "Обновить", + "Update Mask Shape": "Обновить форму маски", + "Update Preset with Current Settings": "Обновить пресет текущими настройками", + "Update Rules from Current Filter": "Обновить условия из текущего фильтра", + "Update Version": "Обновить версию", + "Update Version with Current Settings": "Обновить версию текущими настройками", + "Update with Current Settings": "Обновить текущими настройками", + "Upright": "Выравнивание (Upright)", + "Urban": "Урбан", + "Use Keyword Set": "Использовать набор ключевых слов", + "Use Library Folder": "Использовать папку библиотеки", + "Use camera-specific defaults": "Использовать настройки для конкретной камеры", + "Use the GPU for rendering": "Использовать GPU для рендеринга", + "User Presets": "Пользовательские пресеты", + "Value": "Значение", + "Variance": "Разброс", + "Version": "Версия", + "Version name": "Название версии", + "Versions": "Версии", + "Versions (Shift+V)": "Версии (Shift+V)", + "Vertical": "По вертикали", + "Vertical text": "Вертикальный текст", + "Vibrance": "Красочность", + "Videos": "Видео", + "View": "Вид", + "Vignette": "Виньетка", + "Vignetting": "Виньетирование", + "Vintage": "Винтаж", + "Virtual Copy": "Виртуальная копия", + "Visible": "Видимость", + "Visualize Spots": "Визуализировать пятна", + "Visualize Spots (A)": "Визуализировать пятна (A)", + "Visualize range": "Показать диапазон", + "Vivid": "Яркий", + "Vivid Pop": "Яркий поп", + "Warm Film": "Тёплая плёнка", + "Warm Glow": "Тёплое сияние", + "Warm Print": "Тёплый отпечаток", + "Warm Table": "Тёплый стол", + "Watched folder": "Отслеживаемая папка", + "Watermark": "Водяной знак", + "Wednesday": "Среда", + "What's New": "Что нового", + "When rated or hovered": "При оценке или наведении", + "White Balance": "Баланс белого", + "White Balance Selector": "Пипетка баланса белого", + "White Balance Selector (W)": "Пипетка баланса белого (W)", + "Whites": "Белые", + "Width": "Ширина", + "Width (% of photo)": "Ширина (% от фото)", + "Width (px)": "Ширина (px)", + "Width × Height": "Ширина × высота", + "Window": "Окно", + "Winter Blue": "Зимняя синева", + "XMP Sidecar Preferences": "Настройки XMP-файлов", + "XMP sidecars": "XMP-файлы", + "Year": "Год", + "Yellow": "Жёлтый", + "Yellow Hue": "Тон жёлтого", + "Yellow Luminance": "Яркость жёлтого", + "Yellow Saturation": "Насыщенность жёлтого", + "Yes": "Да", + "You can close this: the download continues, and resumes if interrupted.": "Это окно можно закрыть: загрузка продолжится и возобновится после обрыва.", + "Your library couldn't be opened": "Не удалось открыть вашу библиотеку", + "Zoom": "Масштаб", + "Zoom 100%": "Масштаб 100%", + "Zoom In": "Увеличить", + "Zoom Out": "Уменьшить", + "Zoom to Fit": "Масштаб по размеру окна", + "Zoom to page level": "Масштаб до уровня страницы", + "and": "и", + "comma, separated": "через запятую", + "memory only": "только в памяти", + "none": "нет", + "not available here": "здесь недоступно", + "of the following rules:": "из следующих условий:", + "saved in": "сохранено в", + "this photo": "это фото", + "− harder border · + softer border": "− жёстче край · + мягче край" +} From 95b57d378674579dc2810d366f0c1556a073cc03 Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:20:17 +0400 Subject: [PATCH 016/213] Add Russian language option to menus --- crates/ui-egui/src/menus.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 9e62a382c..77f6449b1 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -18,6 +18,7 @@ pub const LANGUAGE_COMMANDS: &[UiCommand] = &[ ("app.language.traditionalChinese", crate::i18n::Locale::ZhHant.name(), None, "Edit>Language"), ("app.language.japanese", crate::i18n::Locale::Ja.name(), None, "Edit>Language"), ("app.language.portuguese", crate::i18n::Locale::PtBr.name(), None, "Edit>Language"), + ("app.language.russian", crate::i18n::Locale::Ru.name(), None, "Edit>Language"), ]; /// Every UI command: the languages, then everything else. `xtask parity` reads both tables from @@ -35,6 +36,7 @@ pub fn language_from_command(id: &str) -> Option { "app.language.traditionalChinese" => Some(crate::i18n::Locale::ZhHant), "app.language.japanese" => Some(crate::i18n::Locale::Ja), "app.language.portuguese" => Some(crate::i18n::Locale::PtBr), + "app.language.russian" => Some(crate::i18n::Locale::Ru), _ => None, } } From 79fbb6b464c6cb8722eef26196e59772c8e33aea Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:21:31 +0400 Subject: [PATCH 017/213] Add Russian language support in i18n.rs --- crates/ui-egui/src/i18n.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 987631843..f752c9303 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -121,6 +121,7 @@ language_table! { ZhHant, "zh-hant", "繁體中文(台灣)", "Hant", include_str!("../locales/zh-hant.json"); Ja, "ja", "日本語", "Jpan", include_str!("../locales/ja.json"); PtBr, "pt-br", "Português (Brasil)", "Latn", include_str!("../locales/pt-br.json"); + Ru, "ru", "Русский", "Cyrl", include_str!("../locales/ru.json"); } // The settings file stores the BCP-47 code (`"zh-hans"`), never the Rust variant name, so a From 49868693d5f0aa98af2deb1ada73c3b08892f6e2 Mon Sep 17 00:00:00 2001 From: Wondarkus <87841208+wondarkus@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:23:30 +0400 Subject: [PATCH 018/213] Add Russian language support in i18n.rs --- crates/ui-egui/src/i18n.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index f752c9303..5a4909e24 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -467,6 +467,7 @@ mod tests { ("app.language.simplifiedChinese", Locale::ZhHans), ("app.language.japanese", Locale::Ja), ("app.language.portuguese", Locale::PtBr), + ("app.language.russian", Locale::Ru), ]; // One command per language, and every command reachable from the menu table. assert_eq!(commands.len(), Locale::ALL.len()); From 35dd973d91453f0682ebcb83f8b17f5543ae729a Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 8 Oct 2026 07:27:32 +0200 Subject: [PATCH 019/213] M16: add German localization using the existing catalogs Add German language selection and persisted locale support, complete German label and format catalogs, and German release notes. Connect previously untranslated UI text, preserve user names, and size translated buttons to fit. Format cached import source facts in the active UI language. Validate locale coverage, placeholder formats, language persistence, German glyphs, button bounds and cached import language switching. Review native and headless screenshots; run all seven cargo xtask ci gates. Co-Authored-By: Codex (GPT-6) --- README.md | 2 +- ROADMAP.md | 4 +- crates/ui-egui/locales/de-formats.json | 176 ++ crates/ui-egui/locales/de.json | 1725 +++++++++++++++++++ crates/ui-egui/locales/ja-formats.json | 56 +- crates/ui-egui/locales/pt-br-formats.json | 56 +- crates/ui-egui/locales/zh-hans-formats.json | 56 +- crates/ui-egui/locales/zh-hant-formats.json | 56 +- crates/ui-egui/src/credits.rs | 34 +- crates/ui-egui/src/export_task.rs | 2 +- crates/ui-egui/src/headless.rs | 20 + crates/ui-egui/src/i18n.rs | 216 ++- crates/ui-egui/src/import.rs | 124 +- crates/ui-egui/src/lib.rs | 25 +- crates/ui-egui/src/menubar.rs | 19 +- crates/ui-egui/src/menus.rs | 48 +- crates/ui-egui/src/merge.rs | 2 +- crates/ui-egui/src/panels/bottombar.rs | 23 +- crates/ui-egui/src/panels/chips.rs | 59 +- crates/ui-egui/src/panels/detail.rs | 9 +- crates/ui-egui/src/panels/dialogs.rs | 50 +- crates/ui-egui/src/panels/edit.rs | 8 +- crates/ui-egui/src/panels/filterbar.rs | 22 +- crates/ui-egui/src/panels/grid.rs | 15 +- crates/ui-egui/src/panels/left.rs | 9 +- crates/ui-egui/src/panels/masking.rs | 15 +- crates/ui-egui/src/panels/notices.rs | 14 +- crates/ui-egui/src/panels/people.rs | 6 +- crates/ui-egui/src/panels/right.rs | 94 +- crates/ui-egui/src/panels/rules_editor.rs | 54 +- crates/ui-egui/src/panels/settings.rs | 35 +- crates/ui-egui/src/shortcuts.rs | 34 +- crates/ui-egui/src/tasks.rs | 2 +- crates/ui-egui/src/widgets.rs | 7 +- docs/localization-de.md | 36 + docs/localization.md | 6 +- docs/parity.md | 6 +- 37 files changed, 2864 insertions(+), 261 deletions(-) create mode 100644 crates/ui-egui/locales/de-formats.json create mode 100644 crates/ui-egui/locales/de.json create mode 100644 docs/localization-de.md diff --git a/README.md b/README.md index f1f2e4bc8..2f12cf174 100644 --- a/README.md +++ b/README.md @@ -352,7 +352,7 @@ CRAFT_FONTS_DIR=../craft-fonts cargo run --release -p lightcraft Without it LightCraft builds and runs the same, but Chinese and Japanese text have no glyphs. Fonts are never committed to this repo; see [craftrules `standards/fonts.md`](https://github.com/storytold/craftrules/blob/main/standards/fonts.md). -**Interface language:** **Edit → Language** (English, 简体中文, 繁體中文(台灣), 日本語) or **Settings → General → +**Interface language:** **Edit → Language** (English, 简体中文, 繁體中文(台灣), 日本語, Português (Brasil), Deutsch) or **Settings → General → Language**; the choice applies immediately and persists. See [docs/localization.md](docs/localization.md). The web build needs the `wasm32-unknown-unknown` target and the matching `wasm-bindgen` CLI diff --git a/ROADMAP.md b/ROADMAP.md index aeb07567b..f6b98de85 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -29,7 +29,7 @@ duplicate Local entries #22, black GPU exports on an Intel iGPU #78). | **Workflow & library** | ~85% (single machine) | Robust catalog (journal + snapshots, background compaction, crash-tested), 85k-photo libraries stay responsive, Local browsing with automatic cleanup, XMP interop, keywords, smart albums, Move import | Opening an 85k library takes 1.7–4.7 s; no cloud sync (out of scope), no tablet companion (#74, roadmap), shared albums, publish services, tethering | | **Classic modules** | ~30% | Geotagging from GPX track logs, soft proofing (partial), slideshow (basic) | **Map view, Book, Print, Slideshow module, Web, publish services**: ~40 tracker rows ⬜ | | **HDR & video** | 0% | | HDR edit/display/export; video play/trim/edit/export | -| **Platform & robustness** | ~70% | macOS native; Windows/Linux builds; web via WASM; no-panic lints workspace-wide, `unsafe` confined to `crates/sysmem`; failed saves are reported; GPU errors fall back to CPU | Windows installer UI unverified on Windows (PR #79); GPU path proven only on Apple + user reports; English, Simplified/Traditional Chinese and Japanese UI (see docs/localization.md); remaining technical errors; Traditional Chinese uses the Simplified Chinese font until craft-fonts has a TC face; accessibility partial; headless UI tests time out under machine load | +| **Platform & robustness** | ~70% | macOS native; Windows/Linux builds; web via WASM; no-panic lints workspace-wide, `unsafe` confined to `crates/sysmem`; failed saves are reported; GPU errors fall back to CPU | Windows installer UI unverified on Windows (PR #79); GPU path proven only on Apple + user reports; English, Simplified/Traditional Chinese, Japanese, Brazilian Portuguese and German UI (see docs/localization.md); remaining technical errors; Traditional Chinese uses the Simplified Chinese font until craft-fonts has a TC face; accessibility partial; headless UI tests time out under machine load | ### By kind of user @@ -89,7 +89,7 @@ hardening (#78), copyright metadata (#51), GPX geotagging (#60), import tag help | M13 | Merge | HDR merge (deghost), panorama (projections, boundary warp, fill edges), HDR panorama | 10–15 | ✅ | | M14 | Video | import/playback/trim via FilmCraft crates, global edits + presets on video, video export | 6–10 | ⬜ | | M15 | Classic modules | Map, Book, Slideshow, Print, Web; smart collections, stacks, virtual copies, publish services, tethering | 25–40 | 🚧 (smart albums, stacks, virtual copies, compare/survey ✅; Map/Book/Slideshow/Print/Web ⬜) | -| M16 | 1.0 polish | preferences, shortcut editor, accessibility, localization, packaging (dmg/msi/AppImage/web), hardening | 10–20 | 🚧 (settings, keyboard shortcuts sheet, packaging basics ✅; English/Chinese (Simplified, Traditional)/Japanese localisation 🟡; accessibility and further locales ⬜) | +| M16 | 1.0 polish | preferences, shortcut editor, accessibility, localization, packaging (dmg/msi/AppImage/web), hardening | 10–20 | 🚧 (settings, keyboard shortcuts sheet, packaging basics ✅; English/Chinese (Simplified, Traditional)/Japanese/Brazilian Portuguese/German localisation 🟡; accessibility and further locales ⬜) | ## Parity estimate (feature count updated 2026-10-05; effort estimate from 2026-10-02) diff --git a/crates/ui-egui/locales/de-formats.json b/crates/ui-egui/locales/de-formats.json new file mode 100644 index 000000000..5bc6843e0 --- /dev/null +++ b/crates/ui-egui/locales/de-formats.json @@ -0,0 +1,176 @@ +{ + " ({unsure} with several look-alike files: use Locate)": " ({unsure} mit mehreren ähnlichen Dateien: „Fehlende Datei suchen“ verwenden)", + " ({} in Recently Deleted)": " ({} in „Zuletzt gelöscht“)", + " · {:.0}% clipped": " · {:.0}% beschnitten", + " · {failed} couldn't be rendered": " · nicht gerenderte Fotos: {failed}", + " · {skipped} skipped (file exists)": " · {skipped} übersprungen (Datei vorhanden)", + " · {} duplicate{} skipped": " · {} übersprungen (Duplikate){:.0}", + " · {} failed: {}": " · {} fehlgeschlagen: {}", + " · {} moved": " · {} verschoben", + " · {} not readable": " · {} nicht lesbar", + " · {} original{} left at the source": " · Originale an der Quelle verblieben: {}{:.0}", + " — not carried over: {}{}": " — nicht übernommen: {}{}", + "+{} more": "+{} weitere", + ", {failed} file{} not readable": ", nicht lesbare Dateien: {failed}{:.0}", + "; {kept} already had a location": "; bereits verortet: {kept}", + "; {outside} outside its time range": "; {outside} außerhalb des Zeitbereichs", + "@{}: {} PRs, {} commits, +{} / −{} lines (Δ {}), +{} / −{} binary assets, {} – {}": "@{}: {} PRs, {} Commits, +{} / −{} Zeilen (Δ {}), +{} / −{} Binärdateien, {} – {}", + "Add {local_n} to My Photos": "{local_n} zu „Meine Fotos“ hinzufügen", + "Add {} Photo{}": "{} Foto{} hinzufügen", + "Add “{}” to Favorites": "„{}“ zu Favoriten hinzufügen", + "Added {n} photo{} to My Photos": "{n} Foto{} zu „Meine Fotos“ hinzugefügt", + "Added {n} photo{} to “{}”": "{n} Foto{0} zu „{1}“ hinzugefügt", + "Added {what} to {name}": "{what} zu {name} hinzugefügt", + "Adding photos… {} of {}": "Fotos werden hinzugefügt… {} von {}", + "All {n} selected photos move by the same amount.": "Alle {n} ausgewählten Fotos werden um denselben Betrag verschoben.", + "Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "Bereits in der Bibliothek unter „Zuletzt gelöscht“: {} Foto{} ausgewählt · Rechtsklick → Wiederherstellen, oder endgültig löschen, um neu zu importieren", + "Already in the library: {} photo{} selected in All Photos": "Bereits in der Bibliothek: {} Foto{} in „Alle Fotos“ ausgewählt", + "Auto ({})": "Automatisch ({})", + "Auto Import: added {} photo{}": "Automatischer Import: {} Foto{} hinzugefügt", + "Auto Sync: edits apply to {n} photos": "Automatische Synchronisierung: Bearbeitungen gelten für {n} Fotos", + "Automatic ({} MB)": "Automatisch ({} MB)", + "Build 1:1 Previews ({scope})": "1:1-Vorschauen erstellen ({scope})", + "Build Smart Previews ({scope})": "Smart-Vorschauen erstellen ({scope})", + "Build Standard-Sized Previews ({scope})": "Standardvorschauen erstellen ({scope})", + "Building previews for {} photos…": "Vorschauen für {} Fotos werden erstellt…", + "Click {}:1": "Klick {}:1", + "Couldn't open the editor: {e}": "Editor konnte nicht geöffnet werden: {e}", + "Create {n} Virtual Copies": "{n} virtuelle Kopien erstellen", + "Creates {} stacks from {} of {scope}": "Erstellt {} Stapel aus {} von {scope}", + "Decoder: {reason}": "Decoder: {reason}", + "Delete \"{label}\"": "„{label}“ löschen", + "Delete \"{label}\" (and the mask)": "„{label}“ löschen (und die Maske)", + "Delete {n} Photos": "{n} Fotos löschen", + "Delete “{current}”": "„{current}“ löschen", + "Discard Smart Previews ({scope})": "Smart-Vorschauen verwerfen ({scope})", + "Done ({what}): {done} photo{plural}": "Fertig ({what}): {done} Foto{plural}", + "Duplicate \"{label}\"": "„{label}“ duplizieren", + "Example: {example}": "Beispiel: {example}", + "Exported {n} photo{}": "{n} Foto{} exportiert", + "Exported {ok} of {total} photo{}": "{ok} von {total} Foto{} exportiert", + "Exported {} point curve preset{}": "Exportierte Punktkurvenvorgaben: {}{:.0}", + "Exported {} preset{}": "Exportierte Vorgaben: {}{:.0}", + "Exporting {} of {total}": "{} von {total} wird exportiert", + "Filter bar — {active} active filter{}": "Filterleiste — aktive Filter: {active}{:.0}", + "Focus {:.0}": "Schärfe {:.0}", + "Folder “{}” (and its subfolders)": "Ordner „{}“ (und seine Unterordner)", + "Found {n} missing photo{}; {left} still missing{unsure}": "Gefundene fehlende Fotos: {n}{:.0}; weiterhin fehlend: {left}{unsure}", + "Healed {n} dust spot{}": "Reparierte Staubflecken: {n}{:.0}", + "Import cancelled · {} photo{} added": "Import abgebrochen · {} Foto{} hinzugefügt", + "Import failed: {e}": "Import fehlgeschlagen: {e}", + "Imported {n} point curve preset{}": "Importierte Punktkurvenvorgaben: {n}{:.0}", + "Imported {n} preset{}": "Importierte Vorgaben: {n}{:.0}", + "Imported {n} preset{}, {f} file{} not readable": "Importierte Vorgaben: {n}{0:.0}, nicht lesbare Dateien: {f}{1:.0}", + "Imported {profiles} profile{} (Profile browser ▸ their groups)": "Importierte Profile: {profiles}{:.0} (Profilbrowser → jeweilige Gruppen)", + "Imported {} photo{}": "{} Foto{} importiert", + "Insert at the cursor (also written {})": "An Cursorposition einfügen (auch als {} geschrieben)", + "Label Set: {name}": "Farbmarkierungssatz: {name}", + "MIT OR Apache-2.0. Fonts: {} (OFL). Icons: original.": "MIT ODER Apache-2.0. Schriften: {} (OFL). Symbole: eigene Gestaltung.", + "Matches: {}": "Treffer: {}", + "Merging… {stage} {:.0}%": "Wird zusammengefügt… {stage} {:.0}%", + "Move {what} to Recently Deleted?": "{what} nach „Zuletzt gelöscht“ verschieben?", + "Named ({named_n})": "Benannt ({named_n})", + "Painting “{k}”: click photos to add or remove it · Esc stops": "„{k}“ auftragen: Fotos anklicken, um es hinzuzufügen oder zu entfernen · Esc beendet", + "Paste into {n} photo{} — only settings that were copied are pasted": "In {n} Foto{} einfügen — nur kopierte Einstellungen werden eingefügt", + "Preset: {name}": "Vorgabe: {name}", + "Preview only — editing the camera's embedded JPEG ({})": "Nur Vorschau — das eingebettete Kamera-JPEG wird bearbeitet ({})", + "Preview only — {}": "Nur Vorschau — {}", + "Previews ready for {done} photo{plural}": "Vorschauen für {done} Foto{plural} fertig", + "Profile: {}": "Profil: {}", + "Proof Copy ({})": "Softproof-Kopie ({})", + "Proof Preview · {}": "Softproof-Vorschau · {}", + "Quick Develop · {n} photos": "Schnellentwicklung · {n} Fotos", + "Quick Develop: {name}": "Schnellentwicklung: {name}", + "Rated {}": "Bewertet mit {}", + "Reading photos… {done} of {total}": "Fotos werden gelesen… {done} von {total}", + "Reading photos… {} of {}": "Fotos werden gelesen… {} von {}", + "Redo {}": "Wiederholen: {}", + "Remove “{}” from Favorites": "„{}“ aus Favoriten entfernen", + "Removed {what} from {name}": "{what} aus {name} entfernt", + "Rename {n} Photos…": "{n} Fotos umbenennen…", + "Rename “{name}”": "„{name}“ umbenennen", + "Renames “{from}” on {n} photo{} (keywords below it too). Use | for levels, e.g. Travel|Italy. An existing name merges the two.": "Benennt „{from}“ bei {n} Foto{} um (auch untergeordnete Stichwörter). Verwende | für Ebenen, z. B. Reise|Italien. Bei vorhandenem Namen werden beide zusammengeführt.", + "Rendering on the CPU: {why}": "Rendering auf der CPU: {why}", + "Replace {} with:": "{} ersetzen durch:", + "Reset {name}": "{name} zurücksetzen", + "Reset {} Channel": "Kanal {} zurücksetzen", + "Saved export preset “{}”": "Exportvorgabe „{}“ gespeichert", + "Scores {scope} for focus and exposure and finds similar shots taken within seconds of each other (bursts).": "Bewertet {scope} nach Schärfe und Belichtung und findet ähnliche Aufnahmen im Abstand weniger Sekunden (Serien).", + "Show {hidden} hidden location{}": "Ausgeblendete Orte anzeigen: {hidden}{:.0}", + "Showing {n} of {}": "{n} von {} werden angezeigt", + "Smart album: {}": "Smart-Album: {}", + "Tagged {n} photo{} from the tracklog": "{n} Foto{} anhand des GPS-Tracks verortet", + "The SAM 3 download failed: {e}": "SAM-3-Download fehlgeschlagen: {e}", + "Undo {}": "Rückgängig: {}", + "Unknown tag{s} {} stay{} as typed (see Tags)": "Unbekannte Platzhalter{s:.0} {} bleiben unverändert{:.0} (siehe „Platzhalter“)", + "Updated “{name}”": "„{name}“ aktualisiert", + "Version {} — a clean-room, pure-Rust photo library and raw developer.": "Version {} — eine unabhängig entwickelte Fotobibliothek und RAW-Entwicklung in reinem Rust.", + "Version: {}": "Version: {}", + "Virtual copy “{name}” of {of}": "Virtuelle Kopie „{name}“ von {of}", + "Working on {what} for {} photos…": "{what} für {} Fotos wird verarbeitet…", + "none (CPU rendering): {why}": "keine (CPU-Rendering): {why}", + "the {n} selected photos": "die {n} ausgewählten Fotos", + "the {} photos in view": "die {} Fotos in der Ansicht", + "these {count} photos": "diese {count} Fotos", + "{count} smart preview{} · {:.1} MB": "Smart-Vorschauen: {count}{0:.0} · {1:.1} MB", + "{f} folders (and their subfolders)": "{f} Ordner (und ihre Unterordner)", + "{label} Grading": "Farbtonung: {label}", + "{label} {d:+} on every selected photo": "{label} {d:+} für jedes ausgewählte Foto", + "{label} {index} of {n}": "{label} {index} von {n}", + "{label}, {n} photos": "{label}, Fotos: {n}", + "{matching} of {t} photos": "Fotos: {matching} von {t}", + "{matching} photos": "Fotos: {matching}", + "{mode} spot {} of {}": "{mode}: Stelle {} von {}", + "{month}": "{month:02}", + "{month} {year}": "{month:02}/{year}", + "{msg} ({failed} failed)": "{msg} ({failed} fehlgeschlagen)", + "{name} Label": "Farbmarkierung {name}", + "{name} Off": "{name} aus", + "{name} On": "{name} ein", + "{name} opened for editing; it is stacked with the original": "{name} zum Bearbeiten geöffnet; mit dem Original gestapelt", + "{name} — double-click to reset it": "{name} — zum Zurücksetzen doppelklicken", + "{n} change{} couldn't be written to disk: {e}\nIf you quit now, {} lost.": "Nicht auf den Datenträger geschriebene Änderungen: {n}{0:.0}: {e}\nWenn du jetzt beendest, gehen diese Änderungen verloren.{1:.0}", + "{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.": "Ungespeicherte Änderungen: {n}{0:.0} im Arbeitsspeicher, aber nicht auf dem Datenträger: {e}\nLightCraft versucht es automatisch erneut; beim Beenden gingen diese Änderungen verloren.{1:.0}", + "{n} change{} saved in memory but not written to disk: {e} — LightCraft will retry": "Änderungen nur im Arbeitsspeicher gespeichert: {n}{:.0}: {e} — LightCraft versucht es erneut", + "{n} found · {sel} selected": "{n} gefunden · {sel} ausgewählt", + "{n} photos": "Fotos: {n}", + "{n} photos in {} are hidden by the filters above": "Ausgeblendete Fotos in {}: {n} (durch die obigen Filter)", + "{n} photo{}": "{n} Foto{}", + "{n} photo{} match · updates automatically as photos change": "Passende Fotos: {n}{:.0} · wird bei Änderungen an Fotos automatisch aktualisiert", + "{n} photo{} now · updates automatically as photos change": "Aktuelle Fotos: {n}{:.0} · wird bei Änderungen an Fotos automatisch aktualisiert", + "{pid} (missing)": "{pid} (fehlt)", + "{sel_n} selected · {counted}": "{sel_n} ausgewählt · {counted}", + "{stage}… {:.0}%": "{stage}… {:.0}%", + "{verb} {n} Photo{}": "{verb}: {n} Foto{}", + "{weekday}, {day}": "{weekday}, {day}.", + "{weekday}, {day} {month} {year}": "{weekday}, {day}.{month:02}.{year}", + "{what} merge added": "{what}-Zusammenfügung hinzugefügt", + "{what} merge failed: {e}": "{what}-Zusammenfügung fehlgeschlagen: {e}", + "{year}": "{year}", + "{{date:…}} directives: {}": "{{date:…}}-Formatangaben: {}", + "{} and higher": "{} und höher", + "{} contributors · {} commits": "{} Mitwirkende · {} Commits", + "{} failed": "{} fehlgeschlagen", + "{} files": "{} Dateien", + "{} files and {f} folder{}": "{} Dateien und {f} Ordner{:.0}", + "{} item{}": "Elemente: {}{:.0}", + "{} of 4 guides — drag along lines that should be vertical or horizontal.": "{} von 4 Hilfslinien — entlang von Linien ziehen, die senkrecht oder waagerecht sein sollen.", + "{} photos": "Fotos: {}", + "{} photos · exposures {} EV": "{} Fotos · Belichtungen {} EV", + "{} photos · {} · {:.0}° × {:.0}°": "{} Fotos · {} · {:.0}° × {:.0}°", + "{} photo{} not readable": "{} Foto{} nicht lesbar", + "{} sample{}": "Farbproben: {}{:.0}", + "{} sec": "{} s", + "{} spot(s) on this photo": "{} Retuschierstelle(n) in diesem Foto", + "{} — show their photos": "{} — Fotos dieser Person anzeigen", + "· {dups} already in the library": "· {dups} bereits in der Bibliothek", + "· {n} photo{}": "· {n} Foto{}", + "… and {more} more": "… und {more} weitere", + "LightCraft can't decode this raw variant yet ({}). You're editing the camera's embedded JPEG preview, which already includes the camera's picture style (e.g. Monochrome) and white balance.": "LightCraft kann diese RAW-Variante noch nicht dekodieren ({}). Du bearbeitest die eingebettete JPEG-Vorschau der Kamera. Sie enthält bereits deren Bildstil (z. B. Monochrom) und Weißabgleich.", + "Filtered: {label}": "Gefiltert: {label}", + "{n} similar photo{} · View ▸ Clear Filters to see all": "Ähnliche Fotos: {n}{:.0} · Ansicht ▸ Filter löschen zeigt alle Fotos", + "Moved; {} photo(s) relinked": "Verschoben; {} Fotos neu verknüpft", + "{} label (click more labels to show any of them)": "Markierung {} (weitere Markierungen auswählen, um alle passenden Fotos zu zeigen)", + ", {} star{}": ", Sterne: {}{:.0}", + ", {} label": ", Markierung: {}" +} diff --git a/crates/ui-egui/locales/de.json b/crates/ui-egui/locales/de.json new file mode 100644 index 000000000..42464c2c6 --- /dev/null +++ b/crates/ui-egui/locales/de.json @@ -0,0 +1,1725 @@ +{ + " (library default)": " (Bibliotheksstandard)", + " (inverted)": " (umgekehrt)", + " · best of its burst": " · bestes Foto der Serie", + " · in a burst": " · Teil einer Serie", + "\"saved in memory but not written to disk\", the top bar shows a warning, and LightCraft keeps retrying until the": "„im Arbeitsspeicher gespeichert, aber nicht auf den Datenträger geschrieben“, die obere Leiste zeigt eine Warnung, und LightCraft versucht es erneut,", + "#106): a DNG is checked against the raw data, written to a temp file, synced and read back before it gets its": "(Issue #106). Ein DNG wird mit den RAW-Daten verglichen, temporär geschrieben, synchronisiert und zurückgelesen, bevor es seinen", + "% of all commits": "% aller Commits", + "(`gpuReason`, `gpuFallback`), Help ▸ System Info and Settings ▸ Performance say why the GPU isn't": "mit `gpuReason` und `gpuFallback`, Hilfe → Systeminformationen und Einstellungen → Leistung erklären, warum die GPU", + "(e.g. D750, D780, D850, D7500, Z 50) no longer render nearly black or with crushed shadows: their black level was": "etwa D750, D780, D850, D7500 und Z 50, erscheinen nicht mehr fast schwarz oder mit zugelaufenen Tiefen: Ihr Schwarzwert wurde", + "(issue #94): an HTTP request from a web page can no longer carry a command in its body. Lines are capped at": "(Issue #94). Eine HTTP-Anfrage einer Webseite kann so keinen Befehl mehr im Anfrageinhalt transportieren. Zeilen sind auf", + "+ Group": "+ Gruppe", + "+ Rule": "+ Regel", + "0: nothing is rejected.": "0: Nichts wird abgelehnt.", + "1 day": "1 Tag", + "123 on studio-mac)\") instead of letting both write and silently drop each other's edits. A crash never leaves the": "123 auf studio-mac. Beide Programme können nicht gleichzeitig schreiben und gegenseitig Bearbeitungen verlieren. Ein Absturz lässt die", + "1620 × 1080 preview, and import with their metadata: capture time, camera, lens, exposure, GPS and XMP. Their raw": "1620 × 1080 großen Vorschau. Metadaten werden importiert: Aufnahmezeit, Kamera, Objektiv, Belichtung, GPS und XMP. Die RAW-Daten", + "2026-04 or 2026-04-12": "2026-04 oder 2026-04-12", + "23 new built-in presets: Portrait, Landscape, Urban, Food, Seasons, Vintage and B&W toners.": "23 neue eingebaute Vorgaben: Porträt, Landschaft, Stadt, Essen, Jahreszeiten, Vintage und Schwarzweißtonungen.", + "4 MiB and connections at 16.": "4 MiB und gleichzeitige Verbindungen auf 16 begrenzt.", + "630 drivers): on Windows the window and GPU rendering use DirectX 12 only and never load the Vulkan driver unless": "Fenster und GPU-Rendering verwenden unter Windows standardmäßig ausschließlich DirectX 12; der Vulkan-Treiber wird nur auf ausdrückliche Anforderung geladen.", + "= exactly": "= genau", + "A blank template keeps the original names.": "Eine leere Vorlage behält die Originalnamen bei.", + "A change that can't be saved to disk (full or unplugged drive) is no longer silent: the command reports": "Kann eine Änderung wegen eines vollen oder getrennten Laufwerks nicht gespeichert werden, bleibt das nicht mehr unbemerkt: Der Befehl meldet", + "A filter is hiding this view's photos: change it, or clear it (View → Clear Filters)": "Ein Filter blendet die Fotos dieser Ansicht aus: Ändere ihn oder lösche ihn (Ansicht → Filter löschen)", + "A library is open in one program at a time (issue #99): opening a library that LightCraft or `lightcraft-cli` already": "Eine Bibliothek ist nur in einem Programm gleichzeitig geöffnet (Issue #99). Wenn LightCraft oder `lightcraft-cli` sie bereits geöffnet hat,", + "A one-time download of about": "Ein einmaliger Download von etwa", + "A save that fails part-way (a full disk, a network share that drops) no longer looks like a damaged catalog": "Ein teilweise fehlgeschlagener Speichervorgang durch einen vollen Datenträger oder eine getrennte Netzwerkfreigabe hinterlässt keinen scheinbar beschädigten Katalog", + "A sleeping NAS, a dropped network share or a USB drive spinning up no longer freezes the window (issue #104):": "Ein schlafendes NAS, eine getrennte Netzwerkfreigabe oder ein anlaufendes USB-Laufwerk blockieren das Fenster nicht mehr (Issue #104).", + "AI Mask Model Status": "Status des KI-Maskenmodells", + "AI masks with SAM 3 (Object and Describe in the Masking panel): click an object to select it (⌥-click leaves a": "KI-Masken mit SAM 3 über Objekt und Beschreiben im Maskenbereich: Ein Objekt zum Auswählen anklicken; ⌥-Klick lässt einen Teil", + "About": "Über", + "About LightCraft": "Über LightCraft", + "Active": "Aktiv", + "Activity": "Aktivität", + "Actual size": "Originalgröße", + "Add": "Hinzufügen", + "Add Catchlight": "Lichtreflex hinzufügen", + "Add Folder to Local": "Ordner zu „Lokal“ hinzufügen", + "Add Mask": "Maske hinzufügen", + "Add Object Click": "Objektklick hinzufügen", + "Add Point Color Sample": "Punktfarbenprobe hinzufügen", + "Add Red Eye Correction": "Rote-Augen-Korrektur hinzufügen", + "Add Remove Spot": "Retuschierstelle hinzufügen", + "Add Selected Photos": "Ausgewählte Fotos hinzufügen", + "Add a rule (⌥-click: a group)": "Regel hinzufügen (⌥-Klick: eine Gruppe)", + "Add in place": "Am Speicherort hinzufügen", + "Add in place: the library references the files where they are; nothing is copied or moved.": "Am Speicherort hinzufügen: Die Bibliothek verweist auf die Dateien an ihrem aktuellen Speicherort; nichts wird kopiert oder verschoben.", + "Add keyword": "Stichwort hinzufügen", + "Add number": "Nummer hinzufügen", + "Add to": "Hinzufügen zu", + "Add to Album": "Zu Album hinzufügen", + "Add to Favorites": "Zu Favoriten hinzufügen", + "Add to Local": "Zu „Lokal“ hinzufügen", + "Add to My Photos": "Zu „Meine Fotos“ hinzufügen", + "Add to Selected Photos": "Zu ausgewählten Fotos hinzufügen", + "Add to Target Album": "Zu Zielalbum hinzufügen", + "Add to this mask": "Zu dieser Maske hinzufügen", + "Add/Subtract/Intersect Mask": "Maske hinzufügen/subtrahieren/schneiden", + "Added to photos you import that don't already have it.": "Wird importierten Fotos hinzugefügt, die diese Angabe noch nicht enthalten.", + "Adding": "Wird hinzugefügt", + "Advanced search…": "Erweiterte Suche…", + "After": "Nachher", + "After 30 days": "Nach 30 Tagen", + "After 7 days": "Nach 7 Tagen", + "After 90 days": "Nach 90 Tagen", + "After a year": "Nach einem Jahr", + "Album": "Album", + "Albums": "Alben", + "All": "Alle", + "All Metadata": "Alle Metadaten", + "All Metadata…": "Alle Metadaten…", + "All Photos": "Alle Fotos", + "All rights reserved": "Alle Rechte vorbehalten", + "All types": "Alle Typen", + "Alt Text": "Alternativtext", + "Always": "Immer", + "Amount": "Stärke", + "Analyzing the photo for AI masks…": "Foto wird für KI-Masken analysiert…", + "Any Searchable Text": "Beliebiger durchsuchbarer Text", + "Aperture": "Blende", + "Application": "Anwendung", + "Apply Color Label Set": "Farbmarkierungssatz anwenden", + "Apply Filter Preset": "Filtervorgabe anwenden", + "Apply Metadata Preset": "Metadatenvorgabe anwenden", + "Apply Point Curve Preset": "Punktkurvenvorgabe anwenden", + "Apply Preset": "Vorgabe anwenden", + "Apply Settings JSON": "Einstellungen aus JSON anwenden", + "Approved": "Freigegeben", + "Aqua": "Aquamarin", + "Aqua Hue": "Aquamarin – Farbton", + "Aqua Luminance": "Aquamarin – Luminanz", + "Aqua Saturation": "Aquamarin – Sättigung", + "ArtCraft Website": "ArtCraft-Website", + "ArtCraft — more creative apps": "ArtCraft — weitere Kreativ-Apps", + "As Shot": "Wie Aufnahme", + "As shot": "Wie Aufnahme", + "Ascending": "Aufsteigend", + "Aspect": "Seitenverhältnis", + "Aspect Ratio": "Seitenverhältnis", + "Assisted Culling": "Unterstützte Fotoauswahl", + "Assisted Culling…": "Unterstützte Fotoauswahl…", + "Auto": "Auto", + "Auto Advance": "Automatisch weiterschalten", + "Auto Advance Off": "Automatisches Weiterschalten aus", + "Auto Advance On": "Automatisches Weiterschalten ein", + "Auto Advance after rating or flagging": "Nach Bewertung oder Markierung automatisch weiterschalten", + "Auto Advance: move to the next photo after rating or flagging": "Automatisch weiterschalten: Nach Bewertung oder Markierung zum nächsten Foto wechseln", + "Auto Align": "Automatisch ausrichten", + "Auto B&W Mix": "Automatische Schwarzweißmischung", + "Auto Crop": "Automatisch zuschneiden", + "Auto Import": "Automatischer Import", + "Auto Import Now": "Jetzt automatisch importieren", + "Auto Import Settings": "Einstellungen für automatischen Import", + "Auto Mask": "Automatisch maskieren", + "Auto Select": "Automatisch auswählen", + "Auto Settings": "Automatische Einstellungen", + "Auto Straighten": "Automatisch gerade ausrichten", + "Auto Sync": "Automatisch synchronisieren", + "Auto Sync: edits apply to every selected photo. Auto B&W mix. Automatic versions.": "Automatische Synchronisierung auf alle ausgewählten Fotos, automatische Schwarzweißmischung und automatische Versionen.", + "Auto settings applied": "Automatische Einstellungen angewendet", + "Auto-Stack by Capture Time": "Automatisch nach Aufnahmezeit stapeln", + "Auto-Stack by Capture Time…": "Automatisch nach Aufnahmezeit stapeln…", + "Auto-Tag Photos from Tracklog": "Fotos anhand eines GPS-Tracks automatisch verorten", + "Auto-Tag from Tracklog": "Anhand eines GPS-Tracks automatisch verorten", + "Auto-Tag from Tracklog: GPS locations for your photos from a GPX track log, matched by capture time.": "Automatisches Verorten anhand eines GPS-Tracks: GPS-Orte für deine Fotos aus einem GPX-Track, abgeglichen nach Aufnahmezeit.", + "Auto-Tag from Tracklog…": "Anhand eines GPS-Tracks automatisch verorten…", + "Automatic": "Automatisch", + "Automatically Write Changes into XMP": "Änderungen automatisch in XMP schreiben", + "Automatically write changes into XMP sidecars": "Änderungen automatisch in XMP-Begleitdateien schreiben", + "Autumn Gold": "Herbstgold", + "B&W": "S/W", + "B&W Mix": "Schwarzweißmischung", + "B&W Mixer": "Schwarzweißmischer", + "Back": "Zurück", + "Back Up Library…": "Bibliothek sichern…", + "Back to Edit": "Zurück zur Bearbeitung", + "Back to Edit (E)": "Zurück zur Bearbeitung (E)", + "Back to Grid": "Zurück zum Raster", + "Background": "Hintergrund", + "Balance": "Balance", + "Basic": "Grundeinstellungen", + "Before": "Vorher", + "Before/After (Y)": "Vorher/Nachher (Y)", + "Before/After Settings": "Vorher/Nachher-Einstellungen", + "Before/After Split": "Vorher/Nachher geteilt", + "Before/After Split Top/Bottom": "Vorher/Nachher geteilt – oben/unten", + "Before/After Top/Bottom": "Vorher/Nachher – oben/unten", + "Begin Interaction": "Interaktion beginnen", + "Best of Similar Shots": "Beste ähnlicher Aufnahmen", + "Binary assets added": "Hinzugefügte Binärdateien", + "Binary assets removed": "Entfernte Binärdateien", + "Bit depth": "Bittiefe", + "Black & White": "Schwarzweiß", + "Blacks": "Schwarz", + "Bleach Bypass": "Bleach-Bypass", + "Bleached": "Ausgebleicht", + "Blending": "Überblendung", + "Blue": "Blau", + "Blue Hour Boost": "Blaue Stunde verstärken", + "Blue Hue": "Blau – Farbton", + "Blue Luminance": "Blau – Luminanz", + "Blue Primary": "Primärfarbe Blau", + "Blue Saturation": "Blau – Sättigung", + "Blue channel": "Blaukanal", + "Blue/Yellow Fringe": "Blaue/gelbe Farbsäume", + "Blurry photos below the score are flagged as rejects.": "Unscharfe Fotos unterhalb des Grenzwerts werden als abgelehnt markiert.", + "Boundary Warp": "Randverkrümmung", + "Bright & Airy": "Hell und luftig", + "Bring All to Front": "Alle nach vorne bringen", + "Browse Folder": "Ordner durchsuchen", + "Browse Folder…": "Ordner durchsuchen…", + "Browse Profiles": "Profile durchsuchen", + "Browser version (experimental), keeping a library safe (issue #107): File ▸ Back Up Library… downloads the catalog": "Browserversion (experimentell), Schutz der Bibliothek (Issue #107): Datei → Bibliothek sichern… lädt Katalog", + "Browse…": "Durchsuchen…", + "Brush": "Pinsel", + "Brush Settings": "Pinseleinstellungen", + "Brush Stroke": "Pinselstrich", + "Build Previews": "Vorschauen erstellen", + "Build Smart Previews": "Smart-Vorschauen erstellen", + "By Date": "Nach Datum", + "By day (YYYY/YYYY-MM-DD)": "Nach Tag (JJJJ/JJJJ-MM-TT)", + "By month (YYYY/YYYY-MM)": "Nach Monat (JJJJ/JJJJ-MM)", + "CPU threads": "CPU-Threads", + "Calibration": "Kalibrierung", + "Camera": "Kamera", + "Camera make and model": "Kamerahersteller und -modell", + "Cameras and Cards": "Kameras und Karten", + "Cancel": "Abbrechen", + "Cancel AI Mask Model Download": "Download des KI-Maskenmodells abbrechen", + "Cancel Download": "Download abbrechen", + "Cancel Interaction": "Interaktion abbrechen", + "Cancel Preview Build": "Vorschauerstellung abbrechen", + "Candidate": "Kandidat", + "Canon CR2 photos from the EOS 7D, 50D, 60D, 550D, 600D, 1200D, 1300D, 5D Mark II and 1D Mark IV (and other": "Canon-CR2-Fotos der EOS 7D, 50D, 60D, 550D, 600D, 1200D, 1300D, 5D Mark II und 1D Mark IV sowie anderer", + "Canon CR3 files show their full-size embedded JPEG (e.g. 6960 × 4640 on an EOS R6 Mark III) instead of the": "Canon-CR3-Dateien zeigen ihr eingebettetes JPEG in voller Größe, etwa 6960 × 4640 auf einer EOS R6 Mark III, statt der", + "Caption": "Bildunterschrift", + "Capture Date": "Aufnahmedatum", + "Capture date and time in your own format (directives below)": "Aufnahmedatum und -zeit im eigenen Format (Formatangaben unten)", + "Capture date as YYYYMMDD": "Aufnahmedatum als JJJJMMTT", + "Captured": "Aufgenommen", + "Catalog Statistics": "Katalogstatistik", + "Catchlight": "Lichtreflex", + "Change": "Ändern", + "Change Component Mode": "Komponentenmodus ändern", + "Characters not allowed in file names (/ \\ : * ? \" < > |) become -; an existing name gets -1, -2….": "Unzulässige Zeichen in Dateinamen (/ \\ : * ? \" < > |) werden zu -; ein vorhandener Name erhält -1, -2….", + "Check All": "Alle auswählen", + "Check Output Path": "Ausgabepfad prüfen", + "Checking names…": "Namen werden geprüft…", + "Choose Another Library…": "Andere Bibliothek auswählen…", + "Choose Edit Settings to Copy": "Zu kopierende Bearbeitungseinstellungen auswählen", + "Choose Edit Settings to Copy…": "Zu kopierende Bearbeitungseinstellungen auswählen…", + "Choose Folder…": "Ordner auswählen…", + "Choose a photo in the grid or filmstrip": "Wähle ein Foto im Raster oder Filmstreifen", + "Choose…": "Auswählen…", + "Cinematic": "Filmisch", + "City": "Stadt", + "Clarity": "Klarheit", + "Clean Studio": "Sauberes Studio", + "Clear": "Löschen", + "Clear Cache": "Cache leeren", + "Clear Filters": "Filter löschen", + "Clear Guides": "Hilfslinien löschen", + "Clear History": "Verlauf löschen", + "Clear Preview Cache": "Vorschau-Cache leeren", + "Clear Quick Collection": "Schnellsammlung leeren", + "Clear Reference": "Referenz entfernen", + "Clear all": "Alles löschen", + "Clear form": "Formular leeren", + "Cleared History": "Verlauf gelöscht", + "Click Zoom Ratio": "Zoomfaktor beim Klicken", + "Click a tag to insert it. Examples are for IMG_0042.CR3 taken 14 Jan 2026, 05:58:48.": "Klicke auf einen Platzhalter, um ihn einzufügen. Die Beispiele gelten für IMG_0042.CR3, aufgenommen am 14. Januar 2026 um 05:58:48.", + "Click photos in the grid to toggle the keyword; Esc stops": "Klicke auf Fotos im Raster, um das Stichwort umzuschalten; Esc beendet den Vorgang", + "Click the object to select it · ⌥-click leaves a part out": "Objekt zum Auswählen anklicken · ⌥-Klick lässt einen Teil aus", + "Click the photo to pick a colour · ⇧-click adds more": "Klicke auf das Foto, um eine Farbe auszuwählen · ⇧-Klick fügt weitere hinzu", + "Click the photo to pick a colour; ⇧-click adds more (up to 5)": "Klicke auf das Foto, um eine Farbe auszuwählen; ⇧-Klick fügt weitere hinzu (bis zu 5)", + "Click to clear these filters": "Klicken, um diese Filter zu löschen", + "Click to restore · double-click to rename": "Zum Wiederherstellen klicken · zum Umbenennen doppelklicken", + "Clone": "Kopierstempel", + "Close": "Schließen", + "Close Panel": "Bedienfeld schließen", + "Close Window": "Fenster schließen", + "Close all": "Alle schließen", + "Close file": "Datei schließen", + "Cloudy": "Bewölkt", + "Cmd+Shift+R, keeps the photo and clears its edits.)": "mit ⌘⇧R die Bearbeitungen zurück und behält das Foto.", + "Collapse All Stacks": "Alle Stapel einklappen", + "Collapse Stack": "Stapel einklappen", + "Color": "Farbe", + "Color Grading": "Farbtonung", + "Color Label": "Farbmarkierung", + "Color Label Names": "Namen der Farbmarkierungen", + "Color Label Sets": "Farbmarkierungssätze", + "Color Mixer": "Farbmischer", + "Color Noise Reduction": "Farbrauschreduzierung", + "Color Priority": "Farbpriorität", + "Color Range": "Farbbereich", + "Color space": "Farbraum", + "Color space: sRGB (AVIF)": "Farbraum: sRGB (AVIF)", + "Colors": "Farben", + "Colour-label filter with several labels at once; expandable folder tree in Local.": "Farbmarkierungsfilter mit mehreren Markierungen gleichzeitig; aufklappbarer lokaler Ordnerbaum.", + "Colour-range masks: click the photo to sample. Luminance ranges: range bar, smoothness, luminance map.": "Farbbereichsmasken durch Anklicken einer Farbprobe; Luminanzbereiche mit Bereichsleiste, Glättung und Luminanzkarte.", + "Combine files…": "Dateien zusammenführen…", + "Command Journal": "Befehlsprotokoll", + "Comments panel": "Kommentarbereich", + "Commits": "Commits", + "Company": "Unternehmen", + "Compare": "Vergleichen", + "Compare (Shift+C)": "Vergleichen (Umschalt+C)", + "Compare Before and After": "Vorher und Nachher vergleichen", + "Compare needs at least two photos": "Zum Vergleichen sind mindestens zwei Fotos erforderlich", + "Component options": "Komponentenoptionen", + "Compressed Nikon NEFs (lossless and lossy compressed, 12- and 14-bit — e.g. D3200, D5100, D7000, D750, D850, Z 50)": "Komprimierte Nikon-NEFs, verlustfrei oder verlustbehaftet mit 12 oder 14 Bit, etwa D3200, D5100, D7000, D750, D850 und Z 50,", + "Compression": "Komprimierung", + "Concrete Cool": "Kühler Beton", + "Confirm before deleting": "Vor dem Löschen bestätigen", + "Confirm before moving photos to Recently Deleted": "Vor dem Verschieben nach „Zuletzt gelöscht“ bestätigen", + "Constrain Crop": "Zuschnitt beschränken", + "Continue": "Fortfahren", + "Continue Without Saving": "Ohne Speichern fortfahren", + "Contrast": "Kontrast", + "Contributors": "Mitwirkende", + "Convert to DNG": "In DNG konvertieren", + "Convert to DNG, Copy as DNG, Photo Merge and smart previews no longer write straight to the final file (issue": "DNG-Konvertierung, Kopieren als DNG, Fotozusammenfügung und Smart-Vorschauen schreiben nicht mehr direkt in die endgültige Datei", + "Cool Fade": "Kühle Verblassung", + "Cool Morning": "Kühler Morgen", + "Copy": "Kopieren", + "Copy After's Settings to Before": "Nachher-Einstellungen auf Vorher übertragen", + "Copy Before to After": "Vorher auf Nachher übertragen", + "Copy Before's Settings to After": "Vorher-Einstellungen auf Nachher übertragen", + "Copy Edit Settings": "Bearbeitungseinstellungen kopieren", + "Copy History Step Settings to Before": "Einstellungen des Verlaufsschritts auf Vorher übertragen", + "Copy Metadata": "Metadaten kopieren", + "Copy as DNG": "Als DNG kopieren", + "Copy into the library (else use the files where they are)": "In die Bibliothek kopieren (sonst Dateien am Speicherort verwenden)", + "Copy pages": "Seiten kopieren", + "Copy the files (into the library's Originals/, or a folder you choose)": "Dateien kopieren (in Originals/ der Bibliothek oder einen gewählten Ordner)", + "Copy to": "Kopieren nach", + "Copy to Clipboard": "In Zwischenablage kopieren", + "Copy: the files are copied to the folder below and the library uses the copies; the originals are left as they are.": "Kopieren: Die Dateien werden in den folgenden Ordner kopiert und die Bibliothek verwendet die Kopien; die Originale bleiben unverändert.", + "Copyright": "Urheberrecht", + "Copyright Info URL": "URL mit Urheberrechtsinformationen", + "Copyright Status": "Urheberrechtsstatus", + "Copyright status, rights usage terms and copyright info URL in Info, metadata presets and exports.": "Urheberrechtsstatus, Nutzungsbedingungen und URL mit Urheberrechtsinformationen in Infobereich, Metadatenvorgaben und Exporten.", + "Country": "Land", + "Create Album": "Album erstellen", + "Create Album…": "Album erstellen…", + "Create Folder": "Ordner erstellen", + "Create Folder…": "Ordner erstellen…", + "Create New Mask": "Neue Maske erstellen", + "Create PDF from clipboard": "PDF aus Zwischenablage erstellen", + "Create PDF from file…": "PDF aus Datei erstellen…", + "Create PDF from images…": "PDF aus Bildern erstellen…", + "Create Preset": "Vorgabe erstellen", + "Create Preset…": "Vorgabe erstellen…", + "Create Proof Copy": "Softproof-Kopie erstellen", + "Create Smart Album": "Smart-Album erstellen", + "Create Smart Album from Filter…": "Smart-Album aus Filter erstellen…", + "Create Smart Album…": "Smart-Album erstellen…", + "Create Stack": "Stapel erstellen", + "Create Version": "Version erstellen", + "Create Version from Step": "Version aus Schritt erstellen", + "Create Virtual Copy": "Virtuelle Kopie erstellen", + "Creator": "Urheber", + "Creator metadata": "Urheber aus Metadaten", + "Crisp Landscape": "Knackige Landschaft", + "Crisp Vista": "Klare Aussicht", + "Crop": "Zuschneiden", + "Crop & Rotate": "Zuschneiden und drehen", + "Crop & Rotate (C)": "Zuschneiden und drehen (C)", + "Crop Aspect": "Zuschnitt-Seitenverhältnis", + "Crop, Rotate, Geometry": "Zuschnitt, Drehung, Geometrie", + "Cross Process": "Crossentwicklung", + "Culling": "Fotoauswahl", + "Curve": "Kurve", + "Custom": "Benutzerdefiniert", + "Custom template…": "Benutzerdefinierte Vorlage…", + "Custom…": "Benutzerdefiniert…", + "Cut": "Ausschneiden", + "Cut pages": "Seiten ausschneiden", + "Cycle Crop Overlay": "Zuschnittüberlagerung wechseln", + "Cycle Crop Overlay Orientation": "Ausrichtung der Zuschnittüberlagerung wechseln", + "Cycle Info Overlay": "Infoüberlagerung wechseln", + "Cycle Mask Overlay Color": "Farbe der Maskenüberlagerung wechseln", + "Cycle Mask Overlay Mode": "Modus der Maskenüberlagerung wechseln", + "Cylindrical": "Zylindrisch", + "Dark & Moody": "Dunkel und stimmungsvoll", + "Darken": "Abdunkeln", + "Darks": "Dunkle Mitteltöne", + "Date": "Datum", + "Date Groups": "Datumsgruppen", + "Day": "Tag", + "Daylight": "Tageslicht", + "Decrease Brush Feather": "Weiche Pinselkante verringern", + "Decrease Brush Size": "Pinselgröße verringern", + "Deep Sky": "Tiefer Himmel", + "Defringe": "Farbsäume entfernen", + "Deghost Amount": "Geisterbildreduzierung", + "Dehaze": "Dunst entfernen", + "Delete": "Löschen", + "Delete (⌫)": "Löschen (⌫)", + "Delete Album": "Album löschen", + "Delete All Masks": "Alle Masken löschen", + "Delete Color Label Set": "Farbmarkierungssatz löschen", + "Delete Component": "Komponente löschen", + "Delete Export Preset": "Exportvorgabe löschen", + "Delete Filter Preset": "Filtervorgabe löschen", + "Delete Imported Profile": "Importiertes Profil löschen", + "Delete Keyword": "Stichwort löschen", + "Delete Keyword Set": "Stichwortsatz löschen", + "Delete Mask": "Maske löschen", + "Delete Metadata Preset": "Metadatenvorgabe löschen", + "Delete Permanently": "Endgültig löschen", + "Delete Photo": "Foto löschen", + "Delete Photos": "Fotos löschen", + "Delete Point Color": "Punktfarbe löschen", + "Delete Point Color Sample": "Punktfarbenprobe löschen", + "Delete Point Curve Preset": "Punktkurvenvorgabe löschen", + "Delete Preset": "Vorgabe löschen", + "Delete Red Eye": "Rote-Augen-Korrektur löschen", + "Delete Red Eye Correction": "Rote-Augen-Korrektur löschen", + "Delete Spot": "Retuschierstelle löschen", + "Delete Version": "Version löschen", + "Delete all": "Alle löschen", + "Delete all spots": "Alle Retuschierstellen löschen", + "Delete mask": "Maske löschen", + "Delete them": "Löschen", + "Density": "Dichte", + "Depth Range": "Tiefenbereich", + "Descending": "Absteigend", + "Describe": "Beschreiben", + "Describe what to keep": "Beschreibe, was bleiben soll", + "Describe what to leave out": "Beschreibe, was ausgelassen werden soll", + "Describe what to select": "Beschreibe, was ausgewählt werden soll", + "Describe…": "Beschreiben…", + "Deselect": "Auswahl aufheben", + "Deselect All": "Gesamte Auswahl aufheben", + "Desert Heat": "Wüstenhitze", + "Desert Warmth": "Wüstenwärme", + "Desktop": "Schreibtisch", + "Detail": "Detail", + "Detail (D)": "Detail (D)", + "Details": "Details", + "Diagonal": "Diagonal", + "Discard Preview Cache": "Vorschau-Cache verwerfen", + "Display P3": "Display P3", + "Display name": "Anzeigename", + "Display theme": "Oberflächendesign", + "Distortion": "Verzeichnung", + "Document properties…": "Dokumenteigenschaften…", + "Don't enlarge": "Nicht vergrößern", + "Done": "Fertig", + "Download": "Herunterladen", + "Download AI Mask Model": "KI-Maskenmodell herunterladen", + "Download the SAM 3 Model?": "SAM-3-Modell herunterladen?", + "Downloads": "Downloads", + "Drag along the horizon; double-click for Auto": "Entlang des Horizonts ziehen; Doppelklick für Automatik", + "Drag over an eye on the photo; the pupil inside is found automatically.": "Ziehe über ein Auge im Foto; die Pupille darin wird automatisch gefunden.", + "Draw Guides": "Hilfslinien zeichnen", + "Duplicate": "Duplizieren", + "Duplicate Component": "Komponente duplizieren", + "Duplicate Mask": "Maske duplizieren", + "Duplicate and Invert Mask": "Maske duplizieren und umkehren", + "Each / starts a folder level; tags are filled in per photo (a level with missing metadata is \"unknown\").": "Jeder / beginnt eine Ordnerebene; Platzhalter werden pro Foto ausgefüllt (eine Ebene mit fehlenden Metadaten heißt „unknown“).", + "Edge": "Rand", + "Edge setting, and get a sharper zoomed-in pass in the background. The model runs inside LightCraft in pure Rust": "besitzen eine Randeinstellung und werden beim Hineinzoomen im Hintergrund genauer berechnet. Das Modell läuft in LightCraft in reinem Rust", + "Edit": "Bearbeiten", + "Edit (E)": "Bearbeiten (E)", + "Edit Capture Time": "Aufnahmezeit bearbeiten", + "Edit Capture Time…": "Aufnahmezeit bearbeiten…", + "Edit Color Label Names": "Namen der Farbmarkierungen bearbeiten", + "Edit Color Label Names…": "Namen der Farbmarkierungen bearbeiten…", + "Edit Copy for External Editor": "Kopie für externen Editor bearbeiten", + "Edit Date": "Bearbeitungsdatum", + "Edit Info": "Informationen bearbeiten", + "Edit Label Names": "Namen der Farbmarkierungen bearbeiten", + "Edit Label Names…": "Markierungsnamen bearbeiten…", + "Edit Mask": "Maske bearbeiten", + "Edit Mask Component": "Maskenkomponente bearbeiten", + "Edit Sections": "Bearbeitungsbereiche", + "Edit Smart Album": "Smart-Album bearbeiten", + "Edit Smart Album…": "Smart-Album bearbeiten…", + "Edit Spot": "Retuschierstelle bearbeiten", + "Edit in External Editor": "In externem Editor bearbeiten", + "Edit in External Editor (⇧⌘E): a 16-bit TIFF copy, stacked, refreshed when you come back.": "In externem Editor bearbeiten mit ⇧⌘E: Eine 16-Bit-TIFF-Kopie wird gestapelt und bei der Rückkehr aktualisiert.", + "Edit settings copied": "Bearbeitungseinstellungen kopiert", + "Edited": "Bearbeitet", + "Edited or not": "Bearbeitet oder nicht", + "Editing": "Bearbeitung", + "Effect": "Effekt", + "Effects": "Effekte", + "Enable Lens Corrections": "Objektivkorrekturen aktivieren", + "Enable Profile Corrections": "Profilkorrekturen aktivieren", + "End Interaction": "Interaktion beenden", + "English": "English", + "Enter Full Screen": "Vollbildmodus starten", + "Enter a date as YYYY-MM-DD HH:MM:SS": "Datum als JJJJ-MM-TT HH:MM:SS eingeben", + "Erase": "Radieren", + "Every EXIF, GPS and XMP field in the file": "Alle EXIF-, GPS- und XMP-Felder in der Datei", + "Existing previews": "Vorhandene Vorschauen", + "Exit": "Beenden", + "Expand All Stacks": "Alle Stapel ausklappen", + "Expand Stack": "Stapel ausklappen", + "Expand/Collapse Stack": "Stapel aus-/einklappen", + "Export": "Exportieren", + "Export Album with Preset": "Album mit Vorgabe exportieren", + "Export Album…": "Album exportieren…", + "Export As…": "Exportieren als…", + "Export Group…": "Gruppe exportieren…", + "Export Now": "Jetzt exportieren", + "Export Point Curve Presets": "Punktkurvenvorgaben exportieren", + "Export Point Curve Presets…": "Punktkurvenvorgaben exportieren…", + "Export Presets": "Vorgaben exportieren", + "Export Presets…": "Vorgaben exportieren…", + "Export User Presets…": "Benutzervorgaben exportieren…", + "Export file names use the Rename Photos tokens ({title}, {seq:2}, {date:%Y-%m-%d}…), plus new {num}, {folder}, {lens}, {iso}, {rating}, {creator}.": "Exportdateinamen verwenden die Platzhalter der Fotoumbenennung ({title}, {seq:2}, {date:%Y-%m-%d}…) sowie {num}, {folder}, {lens}, {iso}, {rating}, {creator}.", + "Export stopped unexpectedly": "Export unerwartet angehalten", + "Export with Preset": "Mit Vorgabe exportieren", + "Export with Previous": "Mit vorherigen Einstellungen exportieren", + "Exported files are written to a temp file and then renamed into place, so a full disk or an unplugged drive": "Exportdateien werden zunächst temporär geschrieben und anschließend umbenannt. Ein voller Datenträger oder getrenntes Laufwerk", + "Exporting": "Wird exportiert", + "Exports and renders never write over a photo's original (issue #93): exporting into the photo's own folder with": "Export und Rendering überschreiben niemals das Originalfoto (Issue #93). Ein Export in dessen Ordner mit gleichem Namen", + "Exports are never black because of the GPU (issue #78): a GPU render that runs out of device": "Exporte werden wegen GPU-Problemen nicht mehr schwarz (Issue #78): Ein GPU-Rendering mit erschöpftem Gerätespeicher,", + "Export…": "Exportieren…", + "Exposure": "Belichtung", + "Exposure (shutter · aperture · ISO)": "Belichtung (Verschlusszeit · Blende · ISO)", + "Extended Description": "Ausführliche Beschreibung", + "External Editor": "Externer Editor", + "Face Boxes": "Gesichtsrahmen", + "Face boxes": "Gesichtsrahmen", + "Face names written to XMP by Lightroom and other apps show up here.": "Gesichtsnamen, die Lightroom und andere Apps in XMP schreiben, erscheinen hier.", + "Faded Matte": "Verblasst und matt", + "Faded Slide": "Verblasstes Dia", + "Faded Urban": "Verblasste Stadt", + "Faster exports and card imports, with the same protection for what can't be recreated (issue #134): exports,": "Schnellere Exporte und Kartenimporte mit unverändertem Schutz für nicht wiederherstellbare Daten (Issue #134): Exporte,", + "Favorite Preset": "Vorgabe als Favorit markieren", + "Favorite Profile": "Profil als Favorit markieren", + "Favorites": "Favoriten", + "Feather": "Weiche Kante", + "File": "Datei", + "File & date": "Datei und Datum", + "File Format": "Dateiformat", + "File Name": "Dateiname", + "File Path": "Dateipfad", + "File Size": "Dateigröße", + "File Type": "Dateityp", + "File name": "Dateiname", + "Filename": "Dateiname", + "Fill": "Ausfüllen", + "Fill Edges": "Ränder füllen", + "Film": "Film", + "Filmstrip": "Filmstreifen", + "Filmstrip (/)": "Filmstreifen (/)", + "Filter": "Filter", + "Filter Bar": "Filterleiste", + "Filter Presets": "Filtervorgaben", + "Filter bar": "Filterleiste", + "Filter fields": "Filterfelder", + "Find Dust Spots": "Staubflecken finden", + "Find Missing Photos": "Fehlende Fotos suchen", + "Find Missing Photos…": "Fehlende Fotos suchen…", + "Find Similar Photos": "Ähnliche Fotos suchen", + "Find tools and commands…": "Werkzeuge und Befehle suchen…", + "Find…": "Suchen…", + "First": "Erster", + "First commit": "Erster Commit", + "Fit": "Einpassen", + "Fit (F)": "Einpassen (F)", + "Fit on Screen": "An Bildschirm anpassen", + "Fit to width": "An Breite anpassen", + "Fit visible": "Sichtbaren Bereich einpassen", + "Five stars": "Fünf Sterne", + "Flag": "Markierung", + "Flag as Pick": "Als ausgewählt markieren", + "Flag as Reject": "Als abgelehnt markieren", + "Flagged as Pick": "Als ausgewählt markiert", + "Flagged as Reject": "Als abgelehnt markiert", + "Flash": "Blitz", + "Flip H": "H spiegeln", + "Flip Horizontal": "Horizontal spiegeln", + "Flip Overlay (⇧O)": "Überlagerung spiegeln (⇧O)", + "Flip V": "V spiegeln", + "Flip Vertical": "Vertikal spiegeln", + "Flow": "Fluss", + "Fluorescent": "Leuchtstofflicht", + "Focal Length": "Brennweite", + "Focal length": "Brennweite", + "Focus (assisted culling)": "Schärfe (unterstützte Fotoauswahl)", + "Folder": "Ordner", + "Folder name (renamed on disk; its photos follow)": "Ordnername (wird auf dem Datenträger umbenannt; die Fotos folgen)", + "Folders": "Ordner", + "Folders and {date} use the capture time; a photo without one uses today's date.": "Ordner und {date} verwenden die Aufnahmezeit; bei Fotos ohne Aufnahmezeit wird das heutige Datum verwendet.", + "Food": "Essen", + "Forget Unchanged Local Photos": "Unveränderte lokale Fotos vergessen", + "Forget unchanged photos": "Unveränderte Fotos vergessen", + "Form fields panel": "Formularfeldbereich", + "Format": "Format", + "Forward": "Vorwärts", + "Frame time": "Bilddauer", + "Frame update": "Bildaktualisierung", + "Free": "Frei", + "Fresh & Bright": "Frisch und hell", + "Friday": "Freitag", + "Full": "Vollständig", + "Full Screen Preview": "Vollbildvorschau", + "Full screen mode": "Vollbildmodus", + "Full size": "Volle Größe", + "GPS Track Log": "GPS-Track", + "GPU Rendering": "GPU-Rendering", + "GPU rendering": "GPU-Rendering", + "General": "Allgemein", + "Generated demo photo": "Generiertes Demofoto", + "Geometry": "Geometrie", + "Get Develop Settings": "Entwicklungseinstellungen abrufen", + "Global Hue": "Globaler Farbton", + "Global Luminance": "Globale Luminanz", + "Global Saturation": "Globale Sättigung", + "Glossy": "Glänzend", + "Go to History Step": "Zu Verlaufsschritt wechseln", + "Golden": "Goldfarben", + "Golden Glow": "Goldener Glanz", + "Golden Hour": "Goldene Stunde", + "Grab bag": "Namensliste", + "Grain": "Körnung", + "Graphic": "Grafisch", + "Green": "Grün", + "Green Amount": "Grün – Stärke", + "Green Hue": "Grün – Farbton", + "Green Hue High": "Grün – oberer Farbton", + "Green Hue Low": "Grün – unterer Farbton", + "Green Luminance": "Grün – Luminanz", + "Green Primary": "Primärfarbe Grün", + "Green Saturation": "Grün – Sättigung", + "Green channel": "Grünkanal", + "Grid": "Raster", + "Grid Info": "Rasterinformationen", + "Gritty Street": "Raue Straße", + "Group": "Gruppe", + "Group by Date: Automatic": "Nach Datum gruppieren: automatisch", + "Group by Day": "Nach Tag gruppieren", + "Group by Month": "Nach Monat gruppieren", + "Group by Year": "Nach Jahr gruppieren", + "Group by date": "Nach Datum gruppieren", + "Group into Stack": "Zu Stapel gruppieren", + "Group name": "Gruppenname", + "Guided": "Mit Hilfslinien", + "Guided Upright": "Perspektivkorrektur mit Hilfslinien", + "Guided Upright Guides": "Hilfslinien der Perspektivkorrektur", + "HDR Merge": "Zu HDR zusammenfügen", + "HDR Merge Preview": "Vorschau der HDR-Zusammenfügung", + "HDR Panorama Merge": "Zu HDR-Panorama zusammenfügen", + "HDR Panorama Merge Preview": "Vorschau des HDR-Panoramas", + "HDR Panorama with Last Settings": "HDR-Panorama mit letzten Einstellungen", + "HDR Panoramas": "HDR-Panoramen", + "HDR Panorama…": "HDR-Panorama…", + "HDR panorama": "HDR-Panorama", + "HDR panoramas": "HDR-Panoramen", + "HDR with Last Settings": "HDR mit letzten Einstellungen", + "HDR…": "HDR…", + "Has Edits": "Enthält Bearbeitungen", + "Has GPS": "Enthält GPS", + "Heal": "Reparieren", + "Heal the small, soft dark spots sensor dust leaves on smooth areas": "Kleine, weiche dunkle Flecken reparieren, die Sensorstaub auf gleichmäßigen Flächen hinterlässt", + "Height": "Höhe", + "Height (px)": "Höhe (px)", + "Help": "Hilfe", + "Hide LightCraft": "LightCraft ausblenden", + "Hide Mask": "Maske ausblenden", + "Hide Others": "Andere ausblenden", + "Hides this shortcut only; the folder and its photos stay as they are": "Blendet nur diese Verknüpfung aus; der Ordner und seine Fotos bleiben unverändert", + "High": "Hoch", + "High Contrast B&W": "Kontrastreiches Schwarzweiß", + "Highlight": "Hervorheben", + "Highlights": "Lichter", + "Highlights Hue": "Lichter – Farbton", + "Highlights Luminance": "Lichter – Luminanz", + "Highlights Saturation": "Lichter – Sättigung", + "Highlights split": "Lichtergrenze", + "Histogram": "Histogramm", + "History": "Verlauf", + "History & Activity (Y)": "Verlauf und Aktivität (Y)", + "Home": "Persönlicher Ordner", + "Horizontal": "Horizontal", + "Horizontal text": "Horizontaler Text", + "Hours": "Stunden", + "Hue": "Farbton", + "Hue Range": "Farbtonbereich", + "ISO Speed": "ISO-Empfindlichkeit", + "ISO speed": "ISO-Empfindlichkeit", + "If file exists": "Bei vorhandener Datei", + "If your library can't be opened at launch (open in another program, unreadable, on a drive that isn't connected,": "Kann deine Bibliothek beim Start nicht geöffnet werden, etwa weil sie anderweitig geöffnet, unlesbar, auf einem getrennten Laufwerk oder von einer", + "Image": "Bild", + "Import": "Importieren", + "Import Date": "Importdatum", + "Import Photos": "Fotos importieren", + "Import Photos…": "Fotos importieren…", + "Import Point Curve Presets": "Punktkurvenvorgaben importieren", + "Import Point Curve Presets…": "Punktkurvenvorgaben importieren…", + "Import Presets": "Vorgaben importieren", + "Import Presets…": "Vorgaben importieren…", + "Import Profiles": "Profile importieren", + "Import Profiles & Presets…": "Profile und Vorgaben importieren…", + "Import from Device": "Von Gerät importieren", + "Import from Folder…": "Aus Ordner importieren…", + "Import photos with File → Import Photos… (Cmd+Shift+I), or drop them here": "Importiere Fotos über Datei → Fotos importieren… (⌘⇧I) oder lege sie hier ab", + "Import presets from other editors: XMP presets, classic `.lrtemplate` files, \"DNG presets\" from mobile apps and `.zip`": "Vorgaben aus anderen Editoren importieren: XMP-Vorgaben, klassische `.lrtemplate`-Dateien, „DNG-Vorgaben“ aus mobilen Apps und `.zip`-Pakete.", + "Import ▸ Copy verifies every copy, like Move (issue #96): each file is written as a new file, synced to disk and": "Import → Kopieren prüft jede Kopie wie beim Verschieben (Issue #96): Jede Datei wird neu geschrieben, auf den Datenträger synchronisiert", + "Import ▸ Move: photos go into the destination (with the same folders and renaming as Copy, e.g.": "Import → Verschieben: Fotos gehen mit derselben Ordnerstruktur und Umbenennung wie beim Kopieren in den Zielordner, etwa", + "Import: copy to any folder, by day / by month / one folder, rename on import, metadata preset, Copy as DNG.": "Import: In beliebige Ordner kopieren, nach Tag oder Monat oder in einen Ordner, beim Import umbenennen, Metadatenvorgabe und Kopieren als DNG.", + "Imported": "Importiert", + "Imported Photos": "Importierte Fotos", + "Importing": "Wird importiert", + "Importing scans the source for photos; it doesn't add the folder to Local (use Local → Browse Folder… to work in a folder without importing).": "Beim Import wird die Quelle nach Fotos durchsucht; der Ordner wird nicht zu „Lokal“ hinzugefügt (verwende Lokal → Ordner durchsuchen…, um ohne Import in einem Ordner zu arbeiten).", + "In Recently Deleted": "In „Zuletzt gelöscht“", + "In library": "In Bibliothek", + "In memory — nothing is saved": "Im Arbeitsspeicher — nichts wird gespeichert", + "In use": "In Verwendung", + "In-memory session: these settings last until LightCraft quits.": "Sitzung im Arbeitsspeicher: Diese Einstellungen gelten bis zum Beenden von LightCraft.", + "Include subfolders": "Unterordner einbeziehen", + "Increase Brush Feather": "Weiche Pinselkante vergrößern", + "Increase Brush Size": "Pinselgröße vergrößern", + "Info": "Informationen", + "Info (I)": "Informationen (I)", + "Info Overlay Off": "Infoüberlagerung aus", + "Info Overlay: Exposure": "Infoüberlagerung: Belichtung", + "Info Overlay: File": "Infoüberlagerung: Datei", + "Info overlay": "Infoüberlagerung", + "Insert at the cursor": "An Cursorposition einfügen", + "Inspect Photo": "Foto untersuchen", + "Instant ’70s": "Sofortbild der 70er", + "Interface": "Oberfläche", + "Interface language": "Sprache der Oberfläche", + "Intersect": "Schnittmenge", + "Intersect with": "Schnittmenge mit", + "Into one folder": "In einen Ordner", + "Invert": "Umkehren", + "Invert Component": "Komponente umkehren", + "Invert Mask": "Maske umkehren", + "JPEG (Large)": "JPEG (groß)", + "JPEG (Small)": "JPEG (klein)", + "JPEG with the same name (`IMG_0001.CR3` + `IMG_0001.JPG`) no longer share one sidecar: the raw keeps `IMG_0001.xmp`,": "JPEG mit gleichem Namen (`IMG_0001.CR3` + `IMG_0001.JPG`) keine Begleitdatei mehr: RAW verwendet `IMG_0001.xmp`,", + "Join the ArtCraft Discord": "ArtCraft auf Discord beitreten", + "Join the ArtCraft Discord…": "ArtCraft auf Discord beitreten…", + "Join the ArtCraft community on Discord": "Der ArtCraft-Community auf Discord beitreten", + "Keep in Local": "In „Lokal“ behalten", + "Keep it on a drive with room: smart previews are about 1 MB per photo. The thumbnail cache stays in the library.": "Wähle ein Laufwerk mit genügend Platz: Smart-Vorschauen benötigen etwa 1 MB pro Foto. Der Miniaturbild-Cache bleibt in der Bibliothek.", + "Keyboard Shortcuts": "Tastenkürzel", + "Keyboard shortcuts": "Tastenkürzel", + "Keyword": "Stichwort", + "Keyword Painter": "Stichwortpinsel", + "Keyword Set": "Stichwortsatz", + "Keyword Sets": "Stichwortsätze", + "Keyword Suggestions": "Stichwortvorschläge", + "Keyword to paint": "Stichwort zum Auftragen", + "Keywords": "Stichwörter", + "Keywords (K)": "Stichwörter (K)", + "Keywords you add appear here; ⌥1–⌥9 apply them.": "Hinzugefügte Stichwörter erscheinen hier; ⌥1–⌥9 wendet sie an.", + "Label": "Farbmarkierung", + "Landscape": "Landschaft", + "Language": "Sprache", + "Largest long edge the Detail view renders at; larger is sharper on big displays but slower.": "Längste Kante beim Rendern der Detailansicht; größere Werte sind auf großen Bildschirmen schärfer, aber langsamer.", + "Last": "Letzter", + "Last GPU fallback": "Letzter GPU-Fallback", + "Last commit": "Letzter Commit", + "Last loupe render": "Letztes Rendering der Lupenansicht", + "Last view": "Letzte Ansicht", + "Lateral Chromatic Aberration": "Laterale chromatische Aberration", + "Layer": "Ebene", + "Layers": "Ebenen", + "Leave them": "Beibehalten", + "Lens": "Objektiv", + "Lens Blur": "Objektivunschärfe", + "Lens Corrections": "Objektivkorrekturen", + "Level": "Waagerecht", + "Library": "Bibliothek", + "Library Info": "Bibliotheksinformationen", + "Library Originals": "Originale der Bibliothek", + "Library Preferences": "Bibliothekseinstellungen", + "Library State": "Bibliotheksstatus", + "Library saved": "Bibliothek gespeichert", + "Licence:": "Lizenz:", + "Light": "Licht", + "LightCraft 0.2.0 and older refuse it (\"unsupported format … v2\").": "lehnen LightCraft 0.2.0 und ältere Versionen sie ab („unsupported format … v2“).", + "LightCraft Default": "LightCraft-Standard", + "LightCraft Help": "LightCraft-Hilfe", + "LightCraft Preset": "LightCraft-Vorgabe", + "LightCraft Presets": "LightCraft-Vorgaben", + "LightCraft Website": "LightCraft-Website", + "LightCraft couldn't find where to keep your library.": "LightCraft konnte keinen Speicherort für deine Bibliothek finden.", + "LightCraft couldn't open the library at": "LightCraft konnte die Bibliothek nicht öffnen unter", + "LightCraft no longer crashes at launch on Windows PCs whose Vulkan driver is broken (issue #136, e.g. some Intel UHD": "LightCraft stürzt beim Start auf Windows-PCs mit defektem Vulkan-Treiber nicht mehr ab (Issue #136, etwa bestimmte Intel-UHD-630-Treiber).", + "LightCraft on GitHub": "LightCraft auf GitHub", + "LightCraft website": "LightCraft-Website", + "LightCraft — Second Window": "LightCraft — zweites Fenster", + "Lights": "Helle Mitteltöne", + "Limit file size (KB, 0 = off)": "Dateigröße begrenzen (KB, 0 = aus)", + "Line delta": "Zeilendifferenz", + "Linear": "Linear", + "Linear Gradient": "Linearer Verlauf", + "Lines +/−": "Zeilen +/−", + "Lines added": "Hinzugefügte Zeilen", + "Lines deleted": "Gelöschte Zeilen", + "List Albums": "Alben auflisten", + "List Develop Controls": "Entwicklungsregler auflisten", + "List History": "Verlauf auflisten", + "List Presets": "Vorgaben auflisten", + "List Profiles": "Profile auflisten", + "List this folder in Local from now on": "Diesen Ordner ab jetzt unter „Lokal“ auflisten", + "Local": "Lokal", + "Local folders": "Lokale Ordner", + "Local library — no cloud account needed": "Lokale Bibliothek — kein Cloud-Konto erforderlich", + "Locate Missing File…": "Fehlende Datei suchen…", + "Locate Photo": "Foto suchen", + "Location": "Ort", + "Lock Crop Aspect": "Zuschnitt-Seitenverhältnis sperren", + "Long Edge": "Lange Kante", + "Looking for photos…": "Fotos werden gesucht…", + "Lossless": "Verlustfrei", + "Lossy DNG files and Smart Previews open as raw photos.": "Verlustbehaftete DNG-Dateien und Smart-Vorschauen werden als RAW-Fotos geöffnet.", + "Low": "Niedrig", + "Luminance": "Luminanz", + "Luminance Range": "Luminanzbereich", + "Luminar looks: `.lmp` files and `.mplumpack` collections import as presets (grouped by collection); the sliders": "Luminar-Looks: `.lmp`-Dateien und `.mplumpack`-Sammlungen werden als Vorgaben importiert und nach Sammlung gruppiert.", + "Lush Greens": "Sattes Grün", + "Magenta": "Magenta", + "Magenta Hue": "Magenta – Farbton", + "Magenta Luminance": "Magenta – Luminanz", + "Magenta Saturation": "Magenta – Sättigung", + "Make Candidate the Select": "Kandidat als Auswahl verwenden", + "Manual": "Manuell", + "Marquee zoom": "Zoom per Auswahlrahmen", + "Mask Adjustment": "Maskenkorrektur", + "Masking": "Maskieren", + "Masking (M)": "Maskieren (M)", + "Masks": "Masken", + "Match": "Erfülle", + "Match Total Exposures": "Gesamtbelichtungen abgleichen", + "Matte": "Matt", + "Matte Soft": "Sanft matt", + "Med": "Mittel", + "Medium Contrast": "Mittlerer Kontrast", + "Megapixels": "Megapixel", + "Memory Budget": "Speicherbudget", + "Memory Usage": "Speichernutzung", + "Memory budget": "Speicherbudget", + "Memory for caches": "Speicher für Caches", + "Menu": "Menü", + "Merge": "Zusammenfügen", + "Merge Keywords": "Stichwörter zusammenführen", + "Merge into…": "Zusammenführen mit…", + "Merged": "Zusammengefügt", + "Merged PRs": "Zusammengeführte PRs", + "Merging…": "Wird zusammengefügt…", + "Meta's terms, not LightCraft's. Downloading it means accepting them.": "Es gelten die Bedingungen von Meta, nicht von LightCraft. Mit dem Download akzeptierst du sie.", + "Metadata": "Metadaten", + "Metadata Preset": "Metadatenvorgabe", + "Metadata Presets": "Metadatenvorgaben", + "Metadata preset": "Metadatenvorgabe", + "Midpoint": "Mittelpunkt", + "Midtones": "Mitteltöne", + "Midtones Hue": "Mitteltöne – Farbton", + "Midtones Luminance": "Mitteltöne – Luminanz", + "Midtones Saturation": "Mitteltöne – Sättigung", + "Midtones split": "Mitteltontrennung", + "Minimize": "Minimieren", + "Missing Photos": "Fehlende Fotos", + "Missing metadata ({camera}, {title}…) leaves an empty gap; a name that comes out empty keeps the original name.": "Fehlende Metadaten ({camera}, {title}…) hinterlassen eine Lücke; bei einem leeren Ergebnis bleibt der Originalname erhalten.", + "Misty Morning": "Nebliger Morgen", + "Mode": "Modus", + "Model": "Modell", + "Models": "Modelle", + "Modified Date": "Änderungsdatum", + "Monday": "Montag", + "Mono Red Filter": "Monochrom mit Rotfilter", + "Mono Rich": "Kräftig monochrom", + "Mono Soft": "Sanft monochrom", + "Monochrome": "Monochrom", + "Month": "Monat", + "Moody": "Stimmungsvoll", + "Moody Portrait": "Stimmungsvolles Porträt", + "More": "Mehr", + "More preset options": "Weitere Vorgabenoptionen", + "Move": "Verschieben", + "Move Album": "Album verschieben", + "Move Down": "Nach unten verschieben", + "Move Down in Stack": "Im Stapel nach unten verschieben", + "Move Folder": "Ordner verschieben", + "Move Folder To…": "Ordner verschieben nach…", + "Move Mask": "Maske verschieben", + "Move Preset to Group": "Vorgabe in Gruppe verschieben", + "Move Preset to New Group": "Vorgabe in neue Gruppe verschieben", + "Move Up": "Nach oben verschieben", + "Move Up in Stack": "Im Stapel nach oben verschieben", + "Move in Stack": "Im Stapel verschieben", + "Move the files (into the library's Originals/, or a folder you choose), removing them from the source": "Dateien verschieben (in Originals/ der Bibliothek oder einen gewählten Ordner) und aus der Quelle entfernen", + "Move them": "Verschieben", + "Move to": "Verschieben nach", + "Move to Group": "In Gruppe verschieben", + "Move: the files are moved to the folder below; each original (and its XMP sidecar) is removed from the source only after its copy is verified. Duplicates and files that fail stay where they are.": "Verschieben: Die Dateien werden in den folgenden Ordner verschoben; jedes Original (und seine XMP-Begleitdatei) wird erst nach Überprüfung der Kopie aus der Quelle entfernt. Duplikate und fehlgeschlagene Dateien bleiben an ihrem Speicherort.", + "Muted": "Gedämpft", + "Muted Film": "Gedämpfter Film", + "My Photos": "Meine Fotos", + "My Photos Panel": "Bereich „Meine Fotos“", + "Name": "Name", + "Name (A–Z)": "Name (A–Z)", + "Name of the folder the original is in": "Name des Ordners mit dem Original", + "Named": "Benannt", + "Named People": "Benannte Personen", + "Names appear in the label menu, the filter bar and the Info panel, and are written to XMP. Empty = the colour's name.": "Die Namen erscheinen im Farbmarkierungsmenü, in der Filterleiste und im Infobereich und werden in XMP geschrieben. Leer = Name der Farbe.", + "Navigator": "Navigator", + "Needs Work": "Überarbeitung nötig", + "Neon Dusk": "Neondämmerung", + "Neon Night": "Neonnacht", + "Nested group": "Verschachtelte Gruppe", + "Neutral": "Neutral", + "Never": "Nie", + "New": "Neu", + "New Album": "Neues Album", + "New Album…": "Neues Album…", + "New Folder…": "Neuer Ordner…", + "New Group…": "Neue Gruppe…", + "New Smart Album": "Neues Smart-Album", + "New Smart Album from Filter…": "Neues Smart-Album aus Filter…", + "New Smart Album…": "Neues Smart-Album…", + "New album": "Neues Album", + "New album…": "Neues Album…", + "New blank PDF": "Neues leeres PDF", + "New name": "Neuer Name", + "New time": "Neue Zeit", + "New…": "Neu…", + "Next Photo": "Nächstes Foto", + "Night Blue": "Nachtblau", + "Nikon NEFs start from a colour and tone look fitted to the camera's own JPEG, as Sony ARWs do, instead of a muted,": "Nikon-NEFs beginnen mit einem Farb- und Tonwertbild, das wie bei Sony-ARWs an das Kamera-JPEG angepasst ist, statt mit einem gedämpften,", + "Nikon splits into two differently compressed halves still use the preview for now.)": "die Nikon in zwei unterschiedlich komprimierte Hälften teilt, verwenden vorerst weiterhin die Vorschau.", + "No": "Nein", + "No Camera or Card Found": "Keine Kamera oder Karte gefunden", + "No Date Groups": "Keine Datumsgruppen", + "No Stars": "Keine Sterne", + "No Tool": "Kein Werkzeug", + "No automatic versions.": "Keine automatischen Versionen.", + "No camera": "Keine Kamera", + "No contributor data was built into this copy.": "Diese Version enthält keine Daten zu Mitwirkenden.", + "No date": "Kein Datum", + "No dust spots found": "Keine Staubflecken gefunden", + "No edits yet.": "Noch keine Bearbeitungen.", + "No exposure information": "Keine Belichtungsinformationen", + "No filter presets yet": "Noch keine Filtervorgaben", + "No lens data embedded in this file (DNG lens opcodes only).": "Keine Objektivdaten in dieser Datei eingebettet (nur DNG-Objektiv-Opcodes).", + "No masks yet. Choose a mask type above.": "Noch keine Masken. Wähle oben einen Maskentyp.", + "No matching photos": "Keine passenden Fotos", + "No metadata found": "Keine Metadaten gefunden", + "No model credits were built into this copy.": "Diese Version enthält keine Modellangaben.", + "No named people in these photos": "Keine benannten Personen in diesen Fotos", + "No named people yet": "Noch keine benannten Personen", + "No new presets": "Keine neuen Vorgaben", + "No photo selected": "Kein Foto ausgewählt", + "No photos": "Keine Fotos", + "No photos found": "Keine Fotos gefunden", + "No photos in this folder": "Keine Fotos in diesem Ordner", + "No photos match the active filters": "Keine Fotos entsprechen den aktiven Filtern", + "No photos match the filters (View → Clear Filters)": "Keine Fotos entsprechen den Filtern (Ansicht → Filter löschen)", + "No raw photos yet: cameras appear here once their photos are in the library.": "Noch keine RAW-Fotos: Kameras erscheinen hier, sobald ihre Fotos in der Bibliothek sind.", + "No reference photo": "Kein Referenzfoto", + "No usable GPU found: rendering on the CPU": "Keine geeignete GPU gefunden: Rendering auf der CPU", + "No versions yet. Create one to keep this look.": "Noch keine Versionen. Erstelle eine, um diesen Look zu bewahren.", + "Noise": "Rauschen", + "Noise Reduction": "Rauschreduzierung", + "Non-raw photos": "Andere Fotos als RAW", + "None": "Keine", + "Not Now": "Nicht jetzt", + "Nothing to compare": "Nichts zum Vergleichen", + "Nothing to survey": "Nichts für die Übersicht", + "Nudge Develop Value": "Entwicklungswert schrittweise ändern", + "OK": "OK", + "Object": "Objekt", + "Object Mask": "Objektmaske", + "Object and Describe masks use SAM 3, Meta's segmentation model. It isn't part of LightCraft, and everything else works without it.": "Objekt- und Beschreibungsmasken verwenden SAM 3, das Segmentierungsmodell von Meta. Es ist kein Bestandteil von LightCraft; alle anderen Funktionen arbeiten auch ohne dieses Modell.", + "October 2026": "Oktober 2026", + "Off": "Aus", + "Offset X": "Versatz X", + "Offset Y": "Versatz Y", + "Opacity": "Deckkraft", + "Opacity (%)": "Deckkraft (%)", + "Open Library": "Bibliothek öffnen", + "Open Library…": "Bibliothek öffnen…", + "Open in": "Öffnen in", + "Open in Detail": "In Detailansicht öffnen", + "Open the place in OpenStreetMap": "Ort in OpenStreetMap öffnen", + "Open…": "Öffnen…", + "Optics": "Optik", + "Optimize Library": "Bibliothek optimieren", + "Optional name": "Optionaler Name", + "Options": "Optionen", + "Orange": "Orange", + "Orange Hue": "Orange – Farbton", + "Orange Luminance": "Orange – Luminanz", + "Orange Saturation": "Orange – Sättigung", + "Orientation": "Ausrichtung", + "Original": "Original", + "Original + Settings": "Original und Einstellungen", + "Original extension without the dot": "Originalerweiterung ohne Punkt", + "Original file name without its extension": "Originaldateiname ohne Erweiterung", + "Original files": "Originaldateien", + "Original offline · editing the smart preview": "Original offline · Smart-Vorschau wird bearbeitet", + "Original offline · no smart preview": "Original offline · keine Smart-Vorschau", + "Originals": "Originale", + "Other images (JPEG, PNG, TIFF, HEIC…)": "Andere Bilder (JPEG, PNG, TIFF, HEIC…)", + "Overlay": "Überlagerung", + "Overwrite": "Überschreiben", + "Pages": "Seiten", + "Paint": "Auftragen", + "Paint only areas like the one under the brush": "Nur Bereiche wie den unter dem Pinsel bearbeiten", + "Paint over a distraction on the photo to remove it.": "Übermale ein störendes Element im Foto, um es zu entfernen.", + "Paint overlay": "Überlagerung auftragen", + "Panasonic and Leica raws (RW2, RWL and the older RAW files, DMC-LX1 to DC-S1R II) develop from the raw data in": "RAW-Dateien von Panasonic und Leica, RW2, RWL und ältere RAW-Dateien von DMC-LX1 bis DC-S1R II, werden in", + "Panorama": "Panorama", + "Panorama Merge": "Zu Panorama zusammenfügen", + "Panorama Merge Preview": "Panoramavorschau", + "Panorama with Last Settings": "Panorama mit letzten Einstellungen", + "Panoramas": "Panoramen", + "Panorama…": "Panorama…", + "Parametric curve": "Parametrische Kurve", + "Paste": "Einfügen", + "Paste Edit Settings": "Bearbeitungseinstellungen einfügen", + "Paste Metadata": "Metadaten einfügen", + "Paste Selected Settings": "Ausgewählte Einstellungen einfügen", + "Paste Selected Settings…": "Ausgewählte Einstellungen einfügen…", + "Paste Settings": "Einstellungen einfügen", + "Paste Settings from Previous": "Einstellungen vom vorherigen Foto einfügen", + "Paste from Previous": "Vom vorherigen Foto einfügen", + "Paste pages": "Seiten einfügen", + "Pastel Haze": "Pastelldunst", + "People": "Personen", + "Percent": "Prozent", + "Percentage": "Prozentsatz", + "Performance": "Leistung", + "Person": "Person", + "Perspective": "Perspektive", + "Pet Eye": "Tieraugen", + "Pet Eye Catchlight": "Lichtreflex für Tieraugen", + "Photo": "Foto", + "Photo Grid": "Fotoraster", + "Photo Grid (G)": "Fotoraster (G)", + "Photo Merge": "Fotos zusammenfügen", + "Photo menu. The filmstrip has the photo context menu too. Adding a file again that is in Recently Deleted no": "Fotomenü. Auch der Filmstreifen besitzt das Fotokontextmenü. Wird eine Datei erneut hinzugefügt, die unter „Zuletzt gelöscht“ liegt,", + "Photo ▸ Edit in External Editor (⇧⌘E) renders a 16-bit TIFF copy, stacks it with the original and opens it here (an app name on macOS, a program path elsewhere).": "Foto → In externem Editor bearbeiten (⇧⌘E) rendert eine 16-Bit-TIFF-Kopie, stapelt sie mit dem Original und öffnet sie hier (App-Name unter macOS, sonst Programmpfad).", + "Photos": "Fotos", + "Photos in Recently Deleted can be restored from the app: right-click ▸ Restore (or Delete Permanently), also in the": "Fotos unter „Zuletzt gelöscht“ können in der App wiederhergestellt werden: Rechtsklick → Wiederherstellen oder Endgültig löschen, auch im", + "Photos per bracket (0 = auto)": "Fotos pro Belichtungsreihe (0 = automatisch)", + "Photos seen in Local but never added or changed leave the catalog when their folder hasn't been browsed for this long (checked when the library opens). Files stay on disk; browsing the folder shows them again.": "Fotos, die unter „Lokal“ angezeigt, aber nie hinzugefügt oder geändert wurden, verlassen den Katalog, wenn ihr Ordner so lange nicht durchsucht wurde (Prüfung beim Öffnen der Bibliothek). Dateien bleiben auf dem Datenträger; beim Durchsuchen erscheinen sie wieder.", + "Photos that arrive in this folder (tethering, a scanner, a sync app) are added as soon as they're complete.": "Fotos, die in diesem Ordner ankommen (Tethering, Scanner, Synchronisierungs-App), werden hinzugefügt, sobald sie vollständig sind.", + "Photos); importing (also by drag and drop), Find Missing Photos, Build / Discard Smart Previews, the Rename preview,": "Auch Import einschließlich Drag-and-drop, Suche fehlender Fotos, Erstellen und Verwerfen von Smart-Vorschauen, Umbenennungsvorschau,", + "Pick": "Auswahl", + "Pick Flag": "Auswahlmarkierung", + "Pick a colour on the photo with the eyedropper.": "Wähle mit der Pipette eine Farbe im Foto.", + "Pick the sharpest photo of each burst": "Schärfstes Foto jeder Serie auswählen", + "Picked": "Ausgewählt", + "Picks": "Ausgewählte Fotos", + "Pictures": "Bilder", + "Pixels": "Pixel", + "Point Color": "Punktfarbe", + "Point Curve": "Punktkurve", + "Point Curve Presets": "Punktkurvenvorgaben", + "Point curve": "Punktkurve", + "Portrait": "Porträt", + "Position": "Position", + "Preferences": "Einstellungen", + "Preferences…": "Einstellungen…", + "Prepare AI Masks": "KI-Masken vorbereiten", + "Preset": "Vorgabe", + "Preset name": "Name der Vorgabe", + "Presets": "Vorgaben", + "Presets & Profiles": "Vorgaben und Profile", + "Presets (Shift+P)": "Vorgaben (Umschalt+P)", + "Presets and profiles": "Vorgaben und Profile", + "Presets ▾": "Vorgaben ⌄", + "Preview": "Vorschau", + "Preview Build Progress": "Fortschritt der Vorschauerstellung", + "Preview only": "Nur Vorschau", + "Preview size": "Vorschaugröße", + "Previews": "Vorschauen", + "Previous Photo": "Vorheriges Foto", + "Print": "Drucken", + "Print…": "Drucken…", + "Profile": "Profil", + "Profile Browser": "Profilbrowser", + "Profile Distortion": "Profil-Verzeichnung", + "Profile Menu": "Profilmenü", + "Profile Vignetting": "Profil-Vignettierung", + "Profile:": "Profil:", + "Profiles": "Profile", + "Projection": "Projektion", + "Proof copy created": "Softproof-Kopie erstellt", + "Properties": "Eigenschaften", + "Public domain": "Gemeinfrei", + "Pupil Size": "Pupillengröße", + "Purple": "Violett", + "Purple Amount": "Violett – Stärke", + "Purple Hue": "Violett – Farbton", + "Purple Hue High": "Violett – oberer Farbton", + "Purple Hue Low": "Violett – unterer Farbton", + "Purple Luminance": "Violett – Luminanz", + "Purple Saturation": "Violett – Sättigung", + "Put the locations you removed from Local back (no files change)": "Aus „Lokal“ entfernte Orte wiederherstellen (keine Dateien werden geändert)", + "Quality": "Qualität", + "Query Photos": "Fotos abfragen", + "Quick Collection": "Schnellsammlung", + "Quick Collection and target album (B in the grid), keyword sets (⌥1–⌥9), colour-label sets.": "Schnellsammlung und Zielalbum mit B im Raster, Stichwortsätze mit ⌥1–⌥9 und Farbmarkierungssätze.", + "Quick Develop": "Schnellentwicklung", + "Quiet Green": "Ruhiges Grün", + "Quit": "Beenden", + "Quit Anyway": "Trotzdem beenden", + "Quit LightCraft": "LightCraft beenden", + "Quit with unsaved changes?": "Mit ungespeicherten Änderungen beenden?", + "RAW decoding": "RAW-Decodierung", + "Radial": "Radial", + "Radial Gradient": "Radialer Verlauf", + "Radius": "Radius", + "Random": "Zufällig", + "Range": "Bereich", + "Rating": "Bewertung", + "Rating cleared": "Bewertung entfernt", + "Ratings & flags": "Bewertungen und Markierungen", + "Raw": "RAW", + "Raw default": "RAW-Standard", + "Raw defaults": "RAW-Standardeinstellungen", + "Raw files": "RAW-Dateien", + "Raw photos": "RAW-Fotos", + "Raw photos as DNG, with the edits embedded. Size, color and output options don't apply.": "RAW-Fotos als DNG mit eingebetteten Bearbeitungen. Größen-, Farb- und Ausgabeoptionen gelten nicht.", + "Read Metadata from File": "Metadaten aus Datei lesen", + "Read mode": "Lesemodus", + "Read the SAM License": "SAM-Lizenz lesen", + "Reading": "Wird gelesen", + "Reading folder…": "Ordner wird gelesen…", + "Real name": "Echter Name", + "Recent": "Zuletzt verwendet", + "Recent Keywords": "Letzte Stichwörter", + "Recently Added": "Zuletzt hinzugefügt", + "Recently Deleted": "Zuletzt gelöscht", + "Red": "Rot", + "Red Eye": "Rote Augen", + "Red Hue": "Rot – Farbton", + "Red Luminance": "Rot – Luminanz", + "Red Primary": "Primärfarbe Rot", + "Red Saturation": "Rot – Sättigung", + "Red channel": "Rotkanal", + "Red/Cyan Fringe": "Rote/cyanfarbene Farbsäume", + "Redo": "Wiederholen", + "Reference": "Referenz", + "Reference View": "Referenzansicht", + "Reference the files where they are": "Auf Dateien am aktuellen Speicherort verweisen", + "Refine": "Verfeinern", + "Refine AI Mask Detail": "KI-Maskendetails verfeinern", + "Refine Edges": "Ränder verfeinern", + "Refine Mask Edges": "Maskenränder verfeinern", + "Refine Saturation": "Sättigung verfeinern", + "Refining the mask's detail…": "Maskendetails werden verfeinert…", + "Refresh Source": "Quelle aktualisieren", + "Refresh Source (/)": "Quelle aktualisieren (/)", + "Reject": "Ablehnen", + "Reject below focus": "Unter Schärfegrenzwert ablehnen", + "Rejected": "Abgelehnt", + "Rejects": "Abgelehnte Fotos", + "Reliability": "Zuverlässigkeit", + "Relink Photo": "Foto neu verknüpfen", + "Reload Changed Files": "Geänderte Dateien neu laden", + "Reload from Disk": "Vom Datenträger neu laden", + "Remove": "Entfernen", + "Remove (H)": "Entfernen (H)", + "Remove CA": "Chromatische Aberration entfernen", + "Remove Chromatic Aberration": "Chromatische Aberration entfernen", + "Remove Face Box": "Gesichtsrahmen entfernen", + "Remove a filter above, or choose Clear all": "Entferne oben einen Filter oder wähle „Alles löschen“", + "Remove from Album": "Aus Album entfernen", + "Remove from Favorites": "Aus Favoriten entfernen", + "Remove from Local": "Aus „Lokal“ entfernen", + "Remove from Photo": "Von Foto entfernen", + "Remove from Selected Photos": "Von ausgewählten Fotos entfernen", + "Remove from Stack": "Aus Stapel entfernen", + "Remove from Target Album": "Aus Zielalbum entfernen", + "Remove from survey": "Aus Übersicht entfernen", + "Remove location info": "Ortsinformationen entfernen", + "Remove this face box (undo with Edit ▸ Undo)": "Diesen Gesichtsrahmen entfernen (rückgängig über Bearbeiten → Rückgängig)", + "Remove this filter": "Diesen Filter entfernen", + "Remove this group": "Diese Gruppe entfernen", + "Remove this rule": "Diese Regel entfernen", + "Rename": "Umbenennen", + "Rename Album": "Album umbenennen", + "Rename Component": "Komponente umbenennen", + "Rename Folder": "Ordner umbenennen", + "Rename Folder…": "Ordner umbenennen…", + "Rename Folder… and Move Folder To… (Local) can be undone and redone (issue #97): the folder moves back on disk with": "Ordner umbenennen… und Ordner verschieben nach… unter „Lokal“ lassen sich rückgängig machen und wiederholen (Issue #97). Der Ordner wird auf dem Datenträger", + "Rename Keyword": "Stichwort umbenennen", + "Rename Keyword…": "Stichwort umbenennen…", + "Rename Mask": "Maske umbenennen", + "Rename Photos": "Fotos umbenennen", + "Rename Photos never overwrites another photo when only the letter case changes (issue #95): on case-sensitive": "Beim Umbenennen von Fotos wird kein anderes Foto überschrieben, wenn sich nur die Groß-/Kleinschreibung ändert (Issue #95). Auf Laufwerken,", + "Rename Photos reports files it could not move back after a failure (issue #105), e.g. when a network share drops": "Beim Umbenennen meldet LightCraft Dateien, die nach einem Fehler nicht zurückverschoben werden konnten (Issue #105), etwa bei einer getrennten Netzwerkfreigabe", + "Rename Photos…": "Fotos umbenennen…", + "Rename Preset": "Vorgabe umbenennen", + "Rename Preview": "Umbenennungsvorschau", + "Rename Template Tags": "Platzhalter der Umbenennungsvorlage", + "Rename Version": "Version umbenennen", + "Rename…": "Umbenennen…", + "Rendering": "Wird gerendert", + "Rendering…": "Wird gerendert…", + "Reorder Masks": "Masken umordnen", + "Reset": "Zurücksetzen", + "Reset All Curves": "Alle Kurven zurücksetzen", + "Reset Before to Import State": "Vorher auf Importzustand zurücksetzen", + "Reset Control": "Regler zurücksetzen", + "Reset Crop": "Zuschnitt zurücksetzen", + "Reset Edits": "Bearbeitungen zurücksetzen", + "Reset Parametric Curve": "Parametrische Kurve zurücksetzen", + "Reset Section": "Bereich zurücksetzen", + "Reset Tone Curve": "Gradationskurve zurücksetzen", + "Reset all curves: point curves (all channels) and parametric (double-click a channel to reset only that one)": "Alle Kurven zurücksetzen: Punktkurven (alle Kanäle) und parametrische Kurve (Doppelklick auf einen Kanal setzt nur diesen zurück)", + "Reset all edits (Cmd+Shift+R)": "Alle Bearbeitungen zurücksetzen (⌘⇧R)", + "Reshuffle": "Neu mischen", + "Resize Face Box": "Gesichtsrahmengröße ändern", + "Resize to": "Größe ändern auf", + "Resolution (ppi)": "Auflösung (ppi)", + "Restore": "Wiederherstellen", + "Restore Library from Backup…": "Bibliothek aus Sicherung wiederherstellen…", + "Restore Version": "Version wiederherstellen", + "Retouch": "Retuschieren", + "Reverse the order": "Reihenfolge umkehren", + "Revert": "Zurück zum letzten Stand", + "Review": "Überprüfung", + "Review Import": "Import überprüfen", + "Right-click a photo ▸ Set as Reference Photo, then View → Reference View (Shift+R)": "Rechtsklick auf ein Foto → Als Referenzfoto festlegen, dann Ansicht → Referenzansicht (Umschalt+R)", + "Right-click a preset to delete it": "Rechtsklick auf eine Vorgabe, um sie zu löschen", + "Rights Usage Terms": "Nutzungsbedingungen", + "Rotate": "Drehen", + "Rotate Crop": "Zuschnitt drehen", + "Rotate Crop Aspect": "Zuschnitt-Seitenverhältnis drehen", + "Rotate Left": "Nach links drehen", + "Rotate Right": "Nach rechts drehen", + "Roughness": "Unregelmäßigkeit", + "Roundness": "Rundheit", + "Rules": "Regeln", + "SAM 3 download stopped: it resumes where it left off next time.": "SAM-3-Download angehalten: Beim nächsten Mal wird er an derselben Stelle fortgesetzt.", + "Same as raw default": "Wie RAW-Standard", + "Sample Mask Color": "Maskenfarbe aufnehmen", + "Sample a colour on the photo": "Eine Farbe im Foto aufnehmen", + "Saturation": "Sättigung", + "Saturation Range": "Sättigungsbereich", + "Saturday": "Samstag", + "Save": "Speichern", + "Save As…": "Speichern unter…", + "Save Color Label Set": "Farbmarkierungssatz speichern", + "Save Current Filter as Preset…": "Aktuellen Filter als Vorgabe speichern…", + "Save Current Keywords as Set…": "Aktuelle Stichwörter als Satz speichern…", + "Save Export Preset": "Exportvorgabe speichern", + "Save Filter Preset": "Filtervorgabe speichern", + "Save Keyword Set": "Stichwortsatz speichern", + "Save Metadata Preset": "Metadatenvorgabe speichern", + "Save Metadata Preset…": "Metadatenvorgabe speichern…", + "Save Metadata to File": "Metadaten in Datei speichern", + "Save Point Curve Preset": "Punktkurvenvorgabe speichern", + "Save Point Curve…": "Punktkurve speichern…", + "Save as Preset": "Als Vorgabe speichern", + "Save as Set…": "Als Satz speichern…", + "Save as Smart Album…": "Als Smart-Album speichern…", + "Save as set": "Als Satz speichern", + "Save as…": "Speichern unter…", + "Save preset": "Vorgabe speichern", + "Saving metadata to an XMP sidecar another application wrote no longer replaces it (issue #92): LightCraft merges its": "Beim Speichern von Metadaten in einer XMP-Begleitdatei einer anderen Anwendung wird diese nicht mehr ersetzt (Issue #92): LightCraft führt seine", + "Scale": "Skalierung", + "Scan failed": "Durchsuchen fehlgeschlagen", + "Scanning": "Wird durchsucht", + "Scores stay on the photos: filter or make smart albums with Focus and Best of Similar Shots.": "Die Werte bleiben an den Fotos gespeichert: Filtere danach oder erstelle Smart-Alben mit Schärfe und „Beste ähnlicher Aufnahmen“.", + "Screen": "Bildschirm", + "Search": "Suche", + "Search Photos": "Fotos suchen", + "Search…": "Suchen…", + "Seasons": "Jahreszeiten", + "Second Window": "Zweites Fenster", + "Select": "Auswählen", + "Select All": "Alles auswählen", + "Select Luminance Range": "Luminanzbereich auswählen", + "Select Mask": "Maske auswählen", + "Select Photos": "Fotos auswählen", + "Select Spot": "Retuschierstelle auswählen", + "Select a photo to edit": "Wähle ein Foto zum Bearbeiten", + "Select by": "Auswählen nach", + "Select by Flag, Rating or Label": "Nach Markierung, Bewertung oder Farbmarkierung auswählen", + "Select photos, then choose View → Survey (N)": "Wähle Fotos aus, dann Ansicht → Übersicht (N)", + "Select two photos, then choose View → Compare (Shift+C)": "Wähle zwei Fotos aus, dann Ansicht → Vergleichen (Umschalt+C)", + "Selected": "Ausgewählt", + "Selecting…": "Wird ausgewählt…", + "Selenium Tone": "Selentonung", + "Send Feedback…": "Feedback senden…", + "Sepia Tone": "Sepiatonung", + "Sequence number zero-padded to N digits (1–9)": "Laufende Nummer, mit Nullen auf N Stellen aufgefüllt (1–9)", + "Sequence number, counting from the start number": "Laufende Nummer, beginnend mit der Startnummer", + "Services": "Dienste", + "Set": "Festlegen", + "Set Album Cover": "Albumcover festlegen", + "Set Before": "Vorher festlegen", + "Set Color Label": "Farbmarkierung festlegen", + "Set Crop": "Zuschnitt festlegen", + "Set Develop Values": "Entwicklungswerte festlegen", + "Set Flag": "Markierung festlegen", + "Set Mask Adjustments": "Maskenkorrekturen festlegen", + "Set Point Curve": "Punktkurve festlegen", + "Set Profile": "Profil festlegen", + "Set Rating": "Bewertung festlegen", + "Set Top of Stack": "Oberstes Foto im Stapel festlegen", + "Set as Album Cover": "Als Albumcover festlegen", + "Set as Before": "Als Vorher festlegen", + "Set as Reference Photo": "Als Referenzfoto festlegen", + "Set as Target Album": "Als Zielalbum festlegen", + "Set as Target Album (B adds to it)": "Als Zielalbum festlegen (B fügt Fotos hinzu)", + "Set as Top of Stack": "Als oberstes Foto im Stapel festlegen", + "Set date & time": "Datum und Uhrzeit festlegen", + "Set name": "Satzname", + "Set the mix from the photo's colours": "Mischung anhand der Farben des Fotos festlegen", + "Settings": "Einstellungen", + "Settings file problem": "Problem mit Einstellungsdatei", + "Settings files are never quietly reset (issue #103): a damaged `prefs.json`, `presets.json` or `view.json` is kept": "Einstellungsdateien werden nie still zurückgesetzt (Issue #103): Beschädigte `prefs.json`, `presets.json` oder `view.json` bleiben", + "Settings new raw photos start from. Changing them doesn't touch photos already in the library.": "Einstellungen für neue RAW-Fotos. Änderungen wirken sich nicht auf Fotos aus, die bereits in der Bibliothek sind.", + "Settings pasted": "Einstellungen eingefügt", + "Settings to include": "Einzubeziehende Einstellungen", + "Settings…": "Einstellungen…", + "Shade": "Schatten", + "Shadow": "Schatten", + "Shadows": "Tiefen", + "Shadows Hue": "Tiefen – Farbton", + "Shadows Luminance": "Tiefen – Luminanz", + "Shadows Saturation": "Tiefen – Sättigung", + "Shadows Tint": "Tiefen – Tonung", + "Shadows split": "Tiefengrenze", + "Sharpen": "Schärfen", + "Sharpening": "Schärfen", + "Sharpness": "Schärfe", + "Shift": "Verschieben", + "Shift by": "Verschieben um", + "Short Edge": "Kurze Kante", + "Show": "Anzeigen", + "Show All": "Alle einblenden", + "Show Clipping": "Beschnitt anzeigen", + "Show Deghost Overlay": "Geisterbildüberlagerung anzeigen", + "Show Hidden Local Locations": "Ausgeblendete lokale Orte anzeigen", + "Show Luminance Map": "Luminanzkarte anzeigen", + "Show Mask": "Maske anzeigen", + "Show Mask Overlay": "Maskenüberlagerung anzeigen", + "Show Mask Pins": "Maskenpins anzeigen", + "Show Original": "Original anzeigen", + "Show Photo Counts": "Fotoanzahl anzeigen", + "Show Photos with Keyword": "Fotos mit Stichwort anzeigen", + "Show Pins": "Pins anzeigen", + "Show Source": "Quelle anzeigen", + "Show Thumbnails": "Miniaturbilder anzeigen", + "Show destination gamut warning": "Zielfarbraumwarnung anzeigen", + "Show display gamut warning": "Bildschirmfarbraumwarnung anzeigen", + "Show file names": "Dateinamen anzeigen", + "Show in Finder": "Im Finder anzeigen", + "Show on Map": "Auf Karte anzeigen", + "Show overlay (O)": "Überlagerung anzeigen (O)", + "Show ratings, flags and edit badges": "Bewertungen, Markierungen und Bearbeitungssymbole anzeigen", + "Show the Navigator while zoomed in": "Navigator beim Hineinzoomen anzeigen", + "Show the template tags; click one to insert it": "Vorlagenplatzhalter anzeigen; zum Einfügen anklicken", + "Show/Hide Mask": "Maske ein-/ausblenden", + "Show/hide My Photos panel": "Bereich „Meine Fotos“ ein-/ausblenden", + "Shutter speed": "Verschlusszeit", + "Side panels": "Seitliche Bedienfelder", + "Sidecar names": "Namen der Begleitdateien", + "Size": "Größe", + "Size (% of short edge)": "Größe (% der kurzen Kante)", + "Size limit": "Größenbegrenzung", + "Skip": "Überspringen", + "Sky": "Himmel", + "Slideshow": "Diashow", + "Slideshow paused": "Diashow pausiert", + "Slideshow resumed": "Diashow fortgesetzt", + "Slideshow · Space pauses · Esc ends": "Diashow · Leertaste pausiert · Esc beendet", + "Slideshow, second window, All Metadata, System Info.": "Diashow, zweites Fenster, alle Metadaten und Systeminformationen.", + "Smaller, faster catalogs: photos you only looked at in Local (never added, rated or edited) are forgotten once": "Kleinere und schnellere Kataloge: Fotos, die nur unter „Lokal“ angesehen, aber nie hinzugefügt, bewertet oder bearbeitet wurden, werden vergessen,", + "Smart Album": "Smart-Album", + "Smart Album Rule Fields": "Regelfelder für Smart-Alben", + "Smart Preview Status": "Status der Smart-Vorschauen", + "Smart Previews Location": "Speicherort der Smart-Vorschauen", + "Smart albums with a rule editor: match all / any / none, nested groups, 26 fields.": "Smart-Alben mit Regel-Editor: alle, beliebige oder keine Regeln, verschachtelte Gruppen und 26 Felder.", + "Smart preview available": "Smart-Vorschau verfügbar", + "Smart previews": "Smart-Vorschauen", + "Smart previews folder changed": "Ordner für Smart-Vorschauen geändert", + "Smoothness": "Glättung", + "Soft B&W": "Sanftes Schwarzweiß", + "Soft Pastel": "Sanftes Pastell", + "Soft Proofing": "Softproof", + "Soft Skin": "Weiche Haut", + "Sony ARWs from before about 2017 (RX100, RX100 II–V, RX10, NEX, SLT, ILCE-6000, A7 / A7 II / A7R II and their": "Sony-ARWs von vor etwa 2017, RX100, RX100 II–V, RX10, NEX, SLT, ILCE-6000, A7, A7 II, A7R II und verwandte", + "Sony ILCE-7M4 downsized lossless ARWs now decode subsampled YCbCr tiles into linear RGB,": "Verkleinerte verlustfreie ARW-Dateien der Sony ILCE-7M4 werden jetzt aus unterabgetasteten YCbCr-Kacheln in lineares RGB decodiert.", + "Sort": "Sortieren", + "Sort by": "Sortieren nach", + "Source": "Quelle", + "Source code on GitHub": "Quellcode auf GitHub", + "Spherical": "Sphärisch", + "Spiral": "Spirale", + "Split Stack": "Stapel teilen", + "Spring Fresh": "Frühlingsfrische", + "Square Grid": "Quadratisches Raster", + "Square Grid (G toggles)": "Quadratisches Raster (G schaltet um)", + "Square Grid: show file names and formats": "Quadratisches Raster: Dateinamen und Formate anzeigen", + "Stack": "Stapel", + "Stack photos taken within this time of each other:": "Fotos innerhalb dieses Zeitabstands stapeln:", + "Stacks": "Stapel", + "Standard": "Standard", + "Star rating (0–5)": "Sternebewertung (0–5)", + "Start at": "Beginnen bei", + "Start number": "Startnummer", + "Starting": "Wird gestartet", + "Startup": "Beim Start", + "State / Province": "Bundesland / Provinz", + "Stop": "Anhalten", + "Stop Building Previews": "Vorschauerstellung anhalten", + "Stop Using as Target Album": "Nicht mehr als Zielalbum verwenden", + "Stopping…": "Wird angehalten…", + "Straighten": "Gerade ausrichten", + "Straighten Tool": "Gerade-ausrichten-Werkzeug", + "Strong Contrast": "Starker Kontrast", + "Style": "Stil", + "Subfolder": "Unterordner", + "Subfolder name": "Unterordnername", + "Subject": "Motiv", + "Subtract": "Subtrahieren", + "Subtract from this mask": "Von dieser Maske subtrahieren", + "Suggestions": "Vorschläge", + "Summer Haze": "Sommerdunst", + "Sunday": "Sonntag", + "Survey": "Übersicht", + "Survey (N)": "Übersicht (N)", + "Swap": "Tauschen", + "Swap Aspect (X)": "Seitenverhältnis tauschen (X)", + "Swap Before and After": "Vorher und Nachher tauschen", + "Swap Before and After Settings": "Vorher- und Nachher-Einstellungen tauschen", + "Swap Compare Photos": "Vergleichsfotos tauschen", + "Switch light / dark theme": "Helles/dunkles Design wechseln", + "Sync Settings": "Einstellungen synchronisieren", + "System": "System", + "System Info": "Systeminformationen", + "System Info…": "Systeminformationen…", + "System default": "Systemstandard", + "TZ/ZS series, Leica D-Lux, V-Lux and C-Lux, and the GH6, GH7, G9 II, S5 II and S9 generation. They start from a": "und TZ/ZS-Serien, Leica D-Lux, V-Lux und C-Lux sowie GH6, GH7, G9 II, S5 II und S9. Ihr Farb- und Tonwertbild wird", + "Table": "Tabelle", + "Tags": "Platzhalter", + "Tags: see Tags beside the template. Files are renamed on disk (with their XMP sidecars); existing names get -1, -2…": "Platzhalter: Siehe „Platzhalter“ neben der Vorlage. Dateien werden auf dem Datenträger umbenannt (mit ihren XMP-Begleitdateien); vorhandene Namen erhalten -1, -2…", + "Take a snapshot": "Schnappschuss erstellen", + "Targeted Adjustment": "Gezielte Anpassung", + "Targeted adjustment: drag up/down on the photo to adjust the colours there": "Gezielte Anpassung: Im Foto nach oben/unten ziehen, um die dortigen Farben anzupassen", + "Targeted adjustment: drag up/down on the photo to adjust the curve there": "Gezielte Anpassung: Im Foto nach oben/unten ziehen, um die Kurve dort anzupassen", + "Teal & Amber": "Petrol und Bernstein", + "Teal & Orange": "Petrol und Orange", + "Temp": "Temp.", + "Temperature": "Temperatur", + "Template": "Vorlage", + "Temporary session — your library isn't open, and nothing you do here is saved.": "Vorübergehende Sitzung — deine Bibliothek ist nicht geöffnet; hier vorgenommene Änderungen werden nicht gespeichert.", + "Text": "Text", + "Text direction": "Textrichtung", + "Texture": "Struktur", + "The SAM 3 model is installed: Object and Describe masks are ready.": "Das SAM-3-Modell ist installiert: Objekt- und Beschreibungsmaske sind verfügbar.", + "The catalog has a format version (issue #102). Opening a library from an older LightCraft upgrades it; a library": "Der Katalog hat eine Formatversion (Issue #102). Bibliotheken älterer LightCraft-Versionen werden beim Öffnen aktualisiert; eine Bibliothek", + "The control port (`--control`) closes a connection as soon as it receives anything that isn't a JSON request": "Der Steuerport (`--control`) schließt eine Verbindung, sobald er etwas anderes als eine JSON-Anfrage erhält", + "The download didn't work:": "Der Download ist fehlgeschlagen:", + "The number at the end of the original name (IMG_0042 → 0042)": "Nummer am Ende des Originalnamens (IMG_0042 → 0042)", + "The original extension is always added; {ext} only puts it inside the name as well.": "Die Originalerweiterung wird immer angehängt; {ext} fügt sie zusätzlich innerhalb des Namens ein.", + "The original files, unchanged, each with an XMP sidecar holding its edits.": "Unveränderte Originaldateien, jeweils mit einer XMP-Begleitdatei für die Bearbeitungen.", + "The photo has these settings": "Das Foto enthält diese Einstellungen", + "The thumbnail cache only ever counts and deletes its own files (issue #98): a library opened on a folder that": "Der Miniaturbild-Cache zählt und löscht ausschließlich eigene Dateien (Issue #98). Wird eine Bibliothek in einem Ordner mit", + "Theme": "Design", + "They can be restored from Recently Deleted until it is emptied.": "Sie können aus „Zuletzt gelöscht“ wiederhergestellt werden, bis dieser Bereich geleert wird.", + "Thirds": "Drittel", + "This build has no download location for the model yet: put the files in the folder above yourself (see docs/ai-masks.md).": "Diese Version enthält noch keine Downloadquelle für das Modell: Lege die Dateien selbst im obigen Ordner ab (siehe docs/ai-masks.md).", + "This folder is not available (drive disconnected?). Smart previews are not built or used until it is back or you choose another folder.": "Dieser Ordner ist nicht verfügbar (Laufwerk getrennt?). Smart-Vorschauen werden erst wieder erstellt oder verwendet, wenn er verfügbar ist oder du einen anderen Ordner auswählst.", + "This photo can't be opened": "Dieses Foto kann nicht geöffnet werden", + "Threshold": "Schwellenwert", + "Thumbnail cache": "Miniaturbild-Cache", + "Thursday": "Donnerstag", + "Time zone": "Zeitzone", + "Tint": "Tonung", + "Title": "Titel", + "Title metadata": "Titel aus Metadaten", + "Toggle Keyword from Set": "Stichwort aus Satz umschalten", + "Toggle Mask": "Maske umschalten", + "Toggle Section": "Bereich umschalten", + "Toggle Zoom": "Zoom umschalten", + "Tone Curve": "Gradationskurve", + "Tools": "Werkzeuge", + "Top Level": "Oberste Ebene", + "Transfer": "Übertragen", + "Treatment": "Behandlung", + "Triangle": "Dreieck", + "Try Again": "Erneut versuchen", + "Try Saving Again": "Speichern erneut versuchen", + "Tuesday": "Dienstag", + "Tungsten": "Glühlampenlicht", + "Turn Off": "Ausschalten", + "Turn on Include subfolders, or pick another folder under Local": "Aktiviere „Unterordner einbeziehen“ oder wähle unter „Lokal“ einen anderen Ordner", + "Type": "Typ", + "Uncheck All": "Alle abwählen", + "Undo": "Rückgängig", + "Unedited": "Unbearbeitet", + "Unedited raws": "Unbearbeitete RAW-Fotos", + "Unflag": "Markierung entfernen", + "Unflagged": "Unmarkiert", + "Ungroup Stack": "Stapel auflösen", + "Unknown Date": "Unbekanntes Datum", + "Unknown tags stay as typed — check the preview for a {typo}.": "Unbekannte Platzhalter bleiben unverändert — prüfe die Vorschau auf einen {typo}.", + "Unrated": "Unbewertet", + "Unreadable": "Nicht lesbar", + "Update": "Aktualisieren", + "Update Mask Shape": "Maskenform aktualisieren", + "Update Preset with Current Settings": "Vorgabe mit aktuellen Einstellungen aktualisieren", + "Update Rules from Current Filter": "Regeln aus aktuellem Filter aktualisieren", + "Update Version": "Version aktualisieren", + "Update Version with Current Settings": "Version mit aktuellen Einstellungen aktualisieren", + "Update with Current Settings": "Mit aktuellen Einstellungen aktualisieren", + "Updated the edits saved in the external editor": "Im externen Editor gespeicherte Bearbeitungen aktualisiert", + "Upright": "Perspektivkorrektur", + "Urban": "Städtisch", + "Use Keyword Set": "Stichwortsatz verwenden", + "Use Library Folder": "Bibliotheksordner verwenden", + "Use camera-specific defaults": "Kameraspezifische Standardeinstellungen verwenden", + "Use the GPU for rendering": "GPU zum Rendern verwenden", + "User Presets": "Benutzervorgaben", + "Username": "Benutzername", + "Value": "Wert", + "Variance": "Varianz", + "Version": "Version", + "Version name": "Versionsname", + "Versions": "Versionen", + "Versions (Shift+V)": "Versionen (Umschalt+V)", + "Vertical": "Vertikal", + "Vertical text": "Vertikaler Text", + "Vibrance": "Dynamik", + "Videos": "Videos", + "View": "Ansicht", + "Viewing and sharing": "Ansicht und Weitergabe", + "Vignette": "Vignette", + "Vignetting": "Vignettierung", + "Vintage": "Vintage", + "Virtual Copy": "Virtuelle Kopie", + "Visible": "Sichtbar", + "Visualize Spots": "Flecken sichtbar machen", + "Visualize Spots (A)": "Flecken sichtbar machen (A)", + "Visualize range": "Bereich sichtbar machen", + "Vivid": "Lebendig", + "Vivid Pop": "Lebendige Farben", + "Warm Film": "Warmer Film", + "Warm Glow": "Warmer Glanz", + "Warm Print": "Warmer Abzug", + "Warm Table": "Warmer Tisch", + "Watched folder": "Überwachter Ordner", + "Watched-folder auto import; Convert to DNG; Duplicate; Build Standard / 1:1 / Smart Previews.": "Automatischer Import aus überwachten Ordnern, DNG-Konvertierung, Duplizieren und Erstellen von Standard-, 1:1- und Smart-Vorschauen.", + "Watermark": "Wasserzeichen", + "Wednesday": "Mittwoch", + "What's New": "Neuigkeiten", + "When rated or hovered": "Bei Bewertung oder Mauszeigerkontakt", + "White Balance": "Weißabgleich", + "White Balance Selector": "Weißabgleich-Pipette", + "White Balance Selector (W)": "Weißabgleich-Pipette (W)", + "Whites": "Weiß", + "Width": "Breite", + "Width (% of photo)": "Breite (% des Fotos)", + "Width (px)": "Breite (px)", + "Width × Height": "Breite × Höhe", + "Window": "Fenster", + "Winter Blue": "Winterblau", + "Without Saving and Quit (issue #100). It no longer quietly starts a demo session that looked like a reset library and": "Ohne Speichern fortfahren und Beenden (Issue #100). Sie startet keine unbemerkte Demo-Sitzung mehr, die wie eine zurückgesetzte Bibliothek wirkte und", + "XMP Sidecar Preferences": "Einstellungen für XMP-Begleitdateien", + "XMP sidecars": "XMP-Begleitdateien", + "Year": "Jahr", + "Yellow": "Gelb", + "Yellow Hue": "Gelb – Farbton", + "Yellow Luminance": "Gelb – Luminanz", + "Yellow Saturation": "Gelb – Sättigung", + "Yes": "Ja", + "You can close this: the download continues, and resumes if interrupted.": "Du kannst dieses Fenster schließen: Der Download läuft weiter und wird nach Unterbrechungen fortgesetzt.", + "Your Name": "Dein Name", + "Your library couldn't be opened": "Deine Bibliothek konnte nicht geöffnet werden", + "Zoom": "Zoom", + "Zoom 100%": "Zoom 100 %", + "Zoom In": "Vergrößern", + "Zoom Out": "Verkleinern", + "Zoom to Fit": "Zum Einpassen zoomen", + "Zoom to page level": "Auf Seitenebene zoomen", + "`Photos/2026/20260114/20260114_001.jpg`) together with their XMP sidecars; each original leaves the card only after its": "`Photos/2026/20260114/20260114_001.jpg`, zusammen mit ihren XMP-Begleitdateien. Jedes Original verlässt die Karte erst nach", + "`img_1-1.JPG`; on case-insensitive volumes the case change still goes through.": "`img_1-1.JPG`. Auf Laufwerken ohne diese Unterscheidung wird die Schreibweise weiterhin geändert.", + "a full drive) no longer counts as built and Build Smart Previews replaces it.": "oder vollen Laufwerk gilt nicht mehr als fertig und wird beim Erstellen der Smart-Vorschauen ersetzt.", + "a literal %": "ein wörtliches %", + "a removed photo": "einem entfernten Foto", + "afterwards (issue #101): the partial write is cut off before LightCraft retries, so the next launch replays every": "(Issue #101). Vor einem erneuten Versuch wird das Schreibfragment entfernt; beim nächsten Start werden alle Änderungen wiederhergestellt.", + "again. Change the period (or turn it off) in Settings → Performance.": "wieder. Der Zeitraum lässt sich unter Einstellungen → Leistung ändern oder deaktivieren.", + "against the library's originals no longer scans the whole library when nothing is at that path.": "gegen Originale durchsucht nicht mehr die gesamte Bibliothek, wenn an diesem Pfad nichts liegt.", + "all": "alle", + "already has a `thumbs/` folder of other pictures no longer loses them when the cache is trimmed or cleared.": "bereits vorhandenen fremden Bildern unter `thumbs/` geöffnet, bleiben diese beim Leeren oder Verkleinern des Caches erhalten.", + "and": "und", + "and checks the file before using it. Masks keep their selection, so they render and export without the model.": "Vor Verwendung werden die Dateien geprüft. Masken behalten ihre Auswahl und lassen sich ohne das Modell rendern und exportieren.", + "and every imported photo as one zip, and File ▸ Restore Library from Backup… brings it back (the current library": "und alle importierten Fotos als ZIP herunter. Datei → Bibliothek aus Sicherung wiederherstellen… stellt sie wieder her; die aktuelle Bibliothek", + "and never freezes the window. It is optional and not part of LightCraft (Meta's SAM License, about 3.4 GB): the": "und blockiert das Fenster nie. Es ist optional und kein Bestandteil von LightCraft, unter Metas SAM-Lizenz mit etwa 3,4 GB.", + "any": "mindestens eine", + "are empty": "sind leer", + "aren't empty": "sind nicht leer", + "as `.corrupt-