32 Commits
Author SHA1 Message Date
francoisgnandClaude Opus 5.5 dc3263065e Round 3a: an open slider drag ends before a rating or a deletion moves the selection on
CodeRabbit on #2689 (head 02743d187):
- crates/engine/src/cmd/library.rs: `cull` (#2327: a rating, flag or label
  that takes the active photo out of the view) and `photo.delete` /
  `photo.deletePermanently` moved the active photo without ending an open
  interaction, unlike `step` and `library.select`: the drag stayed open with
  no undo step. Each ends it now before its own change, so the drag is its
  own undo step and comes first: undo brings the rating or the photo back,
  with its edit, then the edit (Delete Permanently's undo restores the
  edit too). Test an_open_edit_ends_before_the_selection_moves_on (rating,
  delete, delete permanently) fails without the fix.
- docs/parity-checklist.md (LR-BEHAV-LOCALIZE) and docs/localization.md: list
  every shipped language (Polish, and the others the row lacked; Italian in
  the localization doc).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 05:30:45 +03:00
francoisgnandClaude Opus 5.5 144d900c21 Merge upstream/main (integration batch 43, #2729, #2737, #2412) into round3a-integration
Conflicts:
- apps/lightcraft/src/native_menu.rs: upstream's item swapping (macOS keys
  given up for real, Help menu not swapped, Settings / Quit tracked) with
  KEY-GRID's keymap-aware rows. `Item.label` stays the text the row shows
  (native_item_presentation), so `Item::native_label` reuses it instead of
  formatting it again; Settings / Quit store theirs through
  display_item_label, as upstream's native_label did. No early return after
  a rebuild (upstream).
- crates/ui-egui/src/i18n.rs: upstream's tests (#2729: one test per check
  across languages, gaps reported, never a failure) plus the batch's two new
  ones (dropped-photos notice, Polish counts); production code as merged.
- grid.rs, keyword_list.rs, masking.rs: the translation tests #2729 removed
  stay removed; the batch's new keyword and segmenter tests stay.
- cmd/library.rs, ui-egui/src/lib.rs: both test modules, each with its
  #[cfg(test)].
- xtask/src/main.rs: #2302's test_selected and upstream's translation_gaps.
- docs: tracker / gap rows take both sides' edits, headers list both sides'
  changes, history rows of both sides newest-first. What's New: one section
  per date, newest first (the batch had 10-22 above 10-23, and second 10-21
  and 10-19 sections); no line lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:26:48 +03:00
aiongg b264557916 i18n: missing translations are reported, never a failure (#2729)
A message a catalog lacks, or translates as an empty string, shows in
English and fails nothing:

- build.rs no longer requires every formats catalog to carry the same
  messages, and tr_format! gets a last arm that formats the English
  source, so a message no catalog has builds.
- Coverage tests (every language translates X) and tests of one
  language's exact wording are gone; the language-switching, layout,
  date, placeholder and user-value tests read the catalog's own value
  instead of a hard-coded translation.
- i18n::tests::translation_gaps_report lists, per language, the plain
  and format messages it lacks and its untranslated What's New lines.
  It always passes; cargo xtask ci
  runs it from the test step's binary after the tests.
- catalogs_load checks that every catalog file is a JSON object of
  strings with each key once; it.json named "Dimensions" twice.
2026-10-23 16:42:28 -04:00
francoisgn e297b9ab7a Merge PR #1846: i18n(pl): finish the format messages, counts right for every
# Conflicts:
#	crates/ui-egui/locales/pl-formats.json
#	docs/localization-parity.md
#	docs/localization.md
#	docs/parity-checklist.md
2026-10-22 22:30:06 +03:00
aiongg 05f31758fd Merge PR #1484: Add Vietnamese interface translation
Conflicts:
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep every language plus vi
- docs/localization.md: language list, note links and table excerpt keep every language plus vi
- docs/localization-parity.md: header keeps the newer date and both changes; the newer per-language measurements are kept, Vietnamese row from the PR; links and revision rows are the union
2026-10-22 02:38:28 +00:00
aiongg 0068d9040b Merge PR #2295: i18n: Indonesian (id) interface language
Conflicts:
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep sv, fi and id
- crates/ui-egui/src/panels/keyword_list.rs: code list gains id; Locale::ALL.len() is 17
- docs/localization.md: language list and note links keep every language
- docs/localization-parity.md: header keeps the newer date and both changes; links and revision rows are the union
2026-10-22 02:37:53 +00:00
aiongg d8b2eb60d3 Merge PR #2172: i18n: add Finnish (fi) interface language
Conflicts:
- README.md, docs/localization.md: language lists keep every language, Finnish appended
- assets/ATTRIBUTION.md: union of rows
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep sv and fi
- crates/ui-egui/src/panels/keyword_list.rs: code list gains fi; Locale::ALL.len() is 16
- docs/localization-parity.md: header keeps the newer date and both changes; revision rows are the union
2026-10-22 02:37:29 +00:00
aiongg 4764767694 Merge PR #1855: i18n: Swedish (sv) interface translation
Conflicts:
- README.md, docs/localization.md: language lists keep every language, Swedish appended
- assets/ATTRIBUTION.md: union of rows
- crates/ui-egui/src/i18n.rs: language table and command test list keep it, ka and sv; both tests kept
- crates/ui-egui/src/menus.rs: language commands keep it, ka and sv
- crates/ui-egui/src/panels/keyword_list.rs: code list gains sv; Locale::ALL.len() is 15
2026-10-22 02:37:05 +00:00
Roope GröhnandClaude Sonnet 5.5 494d9d77bb i18n: add Finnish (fi) interface language
Adds Finnish catalogs (fi.json, fi-formats.json), the language table entry
and menu command, Finnish tests, docs and an attribution row. What's New text
stays English, as in most catalogs.

AI-assisted translation (Claude Code), reviewed by a native Finnish speaker.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 18:29:23 +03:00
Reva Doni AprilioandClaude Opus 5.5 816a1112e3 i18n: Indonesian (id) interface language
Adds crates/ui-egui/locales/id.json and id-formats.json: every plain
(2,258) and formats (297) message any catalog has, plus 69 command
labels no catalog translated yet. What's New stays in English.

Indonesian keeps a noun unchanged after a count, so English plural
suffixes are consumed with {:.0}; dates read "Minggu, 20/09/2026".
Menu triangles use → and ⌄ like the other catalogs. 356 labels reuse
the sibling apps' Indonesian wording (PhotoCraft, VectorCraft,
FilmCraft, FigCraft) so the suite reads the same; credited in
ATTRIBUTION.md.

Registers Locale::Id ("Bahasa Indonesia", Latn), the
app.language.indonesian command, the keyword-list catalog check and
an Indonesian switch/persist/format/date test. Docs: localization.md,
localization-id.md, localization-parity.md, ROADMAP.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:17:59 +07:00
Brandon ThomasandClaude Opus 5.5 51d8018e27 Merge PR #1823 (rebased)
Georgian next to Italian in the language table, menus, catalogs list and docs; ka-formats.json lists the format
strings other PRs added, in English until translated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:39:01 -04:00
Patrik LermonandClaude Opus 5.5 9ce2804160 Swedish interface translation (sv)
Adds Swedish (Svenska) as an interface language: 2707 strings, written from the English source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:50:47 +02:00
Brandon ThomasandClaude Opus 5.5 202fcbb525 Merge PR #1823
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:48:43 -04:00
Tomasz LewandowskiandClaude Opus 5.5 458012c464 i18n(pl): finish the format messages, counts right for every number, test and notes
Follow-up to #1100, from #1623:

- The 40 messages of pl-formats.json still in English are translated (mask
  effect presets, slideshow templates and export, Lightroom catalog import,
  keywording, people, panorama merge, the crash message), and two where only
  the arrow had been changed.
- Polish has three plural forms (1 zdjęcie, 2 zdjęcia, 5 zdjęć). 25 messages
  put the number before a genitive plural, which reads wrong for 2–4, 22–24…
  ("Usuń 2 zdjęć"); they now put it after the noun ("Usuń zdjęcia (2)",
  "Zdjęcia: 22"), right for every number. `{n} {noun}` reads `{noun}: {n}` and
  its noun pairs share one form. `{} failed` names a task, not a count.
- Dates read day.month.year (Niedziela, 20.09.2026; 09.2026), not "20 9 2026".
- Test polish_switches_formats_dates_and_reads_every_count: menu and control
  channel, persistence, pl-* tags, dates and time, counts 0–111, user names
  kept verbatim, Polish glyphs in the UI faces.
- docs/localization-pl.md (Polish notes), and the Polish row and links in
  localization.md, localization-parity.md and parity-checklist.md.

The language buttons already wrap on main, so that part of #1623 is not needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DukHNvUY45rjRnvtz91x33
2026-10-15 06:56:20 +02:00
Brandon Thomas 57503c9a4e Merge PR #1198
# Conflicts:
#	ROADMAP.md
#	assets/ATTRIBUTION.md
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/keyword_list.rs
#	crates/ui-egui/src/panels/settings.rs
#	docs/localization-parity.md
#	docs/localization.md
2026-10-15 00:04:52 -04:00
Davit MatchakhelidzeandClaude Opus 5.5 af0c1638fe i18n: Georgian (ka) interface language
Adds Georgian (ქართული, code ka, system tags ka / ka-GE / ka_GE) with
complete plain and formats catalogs, the Edit > Language command and the
Settings choice. Sentence-cased messages go through i18n::upper_first,
which leaves Georgian letters alone (upper-casing Mkhedruli would turn it
into Mtavruli). The Settings language choices wrap now that eleven
endonyms no longer fit on one line, and the face-model Remove button goes
through tr().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-14 22:50:28 +04:00
DUY Tran Nhat (SAE-KIDS)andCursor 07c8e4e68e docs(i18n): document Vietnamese localization
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 17:13:29 +07:00
GabrielePecchioliandClaude Opus 5.5 aec225b6ac Add Italian interface translation (#1192)
Italian (`it`) catalogs: 1,992 interface messages and all 246 format strings
(2,201 of 2,398 messages; What's New left in English like most languages).
410 labels reuse the PhotoCraft, VectorCraft, PdfCraft and WordCraft Italian
catalogs where the meaning fits a photo app; 46 were retranslated.

Counts follow a label ("Foto importate: 3") because Italian adjectives agree
with the number; the English plural suffix is consumed with {:.0}.

Language table entry, menu command, three tests mirroring Ukrainian's, the
button-fit test extended to Italian, and the Settings language buttons now
wrap so an eleventh language doesn't widen the dialog past a short window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:45:26 +02:00
LosminekandClaude Opus 5.5 358a19c755 i18n: add Czech (cs) interface language
Add the Czech catalog (2,056 UI messages, 246 format templates), the
language-menu entry, locale detection (cs, cs-CZ, cs_CZ.UTF-8) and
regression tests for switching, persistence, dates, placeholders and
glyphs. Translate a few face/model, person and HDR/PDF captions through
the existing tr() interface. Settings > Language now wraps its buttons,
since twelve language names no longer fit a narrow window.

What's New prose stays English, like most catalogs. AI-assisted, with
an independent review pass; not yet proofread by a native speaker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:32:45 +02:00
Brandon Thomas 53c056daf3 Merge PR #564
# Conflicts:
#	README.md
#	crates/ui-egui/src/i18n.rs
#	docs/localization.md
#	docs/parity.md
2026-10-09 20:56:56 -04:00
Brandon Thomas 0fc81747cb Merge PR #541 2026-10-09 20:56:55 -04:00
Dmytro MatviichukandCodex b08f80700f M16: add Ukrainian interface translation (#558)
Add Ukrainian catalogs, locale selection, context-aware labels and regression coverage.
Record permissive sibling-catalog provenance in the existing attribution index.

Co-authored-by: Codex <noreply@openai.com>
2026-10-09 20:07:44 +02:00
Brandon ThomasandClaude Opus 5.5 3978a0ac9a French catalog: plurals, paintable separators, percent format, two new strings
- "photo(s){}" printed "photos(s)s"-style text: the plural suffix now follows the noun ("photo{}"),
  as the Spanish catalog does; smart previews repeat it ({0}).
- Menu triangles (▸) in translations replaced by → (the shipped fonts have no ▸ glyph; test
  translated_catalogs_do_not_use_missing_menu_triangles).
- "{:.0} %" -> "{:.0}%" so the Merging progress test matches every language.
- Translate the two strings main added since the branch (Camera local tone mapping).
- README, docs/localization.md and the LR-BEHAV-LOCALE parity row list French.
- Test: French plural formatting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:57:36 -04:00
Nathaniel LedfordandClaude Opus 5.5 451e3cd28c LRC-LIB-SMARTCOLL: document message contexts for translators
localization.md explains "<context>|<English>" catalog keys and the one
context today (smart album value); localization-ja.md notes あり / なし.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:54:56 -04:00
Claude 0b587a0a8a Fix #371: Spanish interface
A Spanish (es) catalog and formats catalog covering the same messages as
the Brazilian Portuguese, Japanese and Simplified Chinese catalogs (1595
messages, 180 formats), an Edit > Language > Español command
(app.language.spanish), es tag tests (es, es-ES, es_MX.UTF-8, es-419),
docs/localization-es.md with the terminology used, and the language
lists in docs/localization.md, parity and What's New.

Instructions use infinitives or impersonal forms (no usted). Plural
endings follow the existing convention: the English "s" passes through
where Spanish adds one, else {:.0} swallows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8
2026-10-08 20:20:17 +00:00
Claude 0634782fbc i18n: German and Russian follow-ups (#265, #264)
- #265's German coverage test failed whenever any catalog key, command or
  control label, or line of What's New lacked German, so every feature PR
  would have had to ship German. It reports those gaps for every language
  now (display_label_gaps_are_reported), as the project's other gap test
  does; docs/localization.md says how release-note lines are matched and
  that an English copy in a formats catalog marks an untranslated message.
- Russian: docs/localization-ru.md, the language lists in
  docs/localization.md and docs/parity.md, ru tag tests.
- What's new: German and Russian UI. Removes a status sentence #265 put
  in parity.md's Top gaps list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8
2026-10-08 18:26:56 +00:00
MarcelandCodex 35dd973d91 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) <noreply@openai.com>
2026-10-08 07:27:32 +02:00
RicSchonfelderandDeepSeek V4 Flash fb01966a00 i18n: add Brazilian Portuguese (pt-br) UI translation
Add pt-br as a fifth interface language beside English, Simplified
Chinese, Traditional Chinese (Taiwan) and Japanese.

`crates/ui-egui/locales/pt-br.json` and `pt-br-formats.json` carry the
messages and the format strings; the language table gains the entry
(`PtBr, "pt-br", "Português (Brasil)", "Latn", ...`), and Edit > Language
plus `app.language.portuguese` select it. Locale tags pt-BR, pt_BR.UTF-8
and pt all normalize to the same language.

Translation follows pt-BR UI conventions (Adobe terms: revelação,
balanço de branco, máscara, cópia virtual…); proper nouns and file
formats stay as written (LightCraft, ArtCraft, GitHub, DNG, TIFF, XMP).
Placeholders, format specs and punctuation match the English source; the
English plural suffix is consumed with `{:.0}` where Portuguese does not
need it.

Tests: every_language_has_a_catalog_covering_the_source,
catalogs_agree_on_placeholders_and_report_gaps, locale_tags_normalize
and the painted-window test now cover pt-br. Docs: localization.md, a
new localization-pt-br.md and the parity rows.

Co-Authored-By: DeepSeek V4 Flash (opencode) <noreply@opencode.ai>
2026-10-07 17:03:20 -03:00
ca0c569863 LR-BEHAV-LOCALIZE: Traditional Chinese (Taiwan) UI, stock preset and date localisation
Port of PR #198 onto the language table:
- Traditional Chinese (Taiwan) catalogs as a table entry: code zh-hant, name
  繁體中文(台灣), script Hant; zh-TW / zh-HK / zh-Hant tags reach it; command
  app.language.traditionalChinese
- display-only helpers: builtin_label (stock preset/profile names), history_label
  (generated history steps, user preset names verbatim), source_label (library
  headings, album names verbatim), date_group_label / display_time
- menu, dialog and panel strings (profiles, presets, curves, crop, versions,
  history, grid, date sidebar, About links) go through the catalogs
- a language command and ui.set apply the language at once; a native-menu test
  checks that every language switch rebuilds the menu titles

Adapted while porting (Claude):
- date headings and capture times are localised for every language from its
  *-formats.json date patterns, not only for zh-TW; ja and zh-hans gain the date
  patterns, weekday names and "Unknown Date", and zh-hans the new menu messages
  (ja's come from the PR)
- plain catalogs may cover different messages (English fallback); the test
  reports the gaps instead of failing. The test requiring every engine command
  label to be translated is dropped
- preset groups stay grouped by name: a user preset in a stock-named group is
  not split into a separate group (the group header is translated when it holds
  stock presets); the PR's split was a side effect of labelling
- no Traditional Chinese face in craft-fonts yet: Hant prefers the Simplified
  Chinese face (which carries the Traditional characters) over the Japanese ones
- zh-hant translations for the messages main added since the PR branched

Co-authored-by: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:54:26 -04:00
Brandon ThomasandClaude Opus 5.5 87a58b4183 LR-BEHAV-LOCALIZE: one Language menu entry per language; parity reads the language table
- UI_COMMANDS still listed app.language.english/japanese next to the new
  LANGUAGE_COMMANDS table, so Edit > Language showed English and 日本語 twice
- LANGUAGE_COMMANDS is `pub const`, the form xtask parity scans for; a test
  reads the real menus.rs and checks every language id is found once
- the Simplified Chinese command is app.language.simplifiedChinese, in line
  with the other camelCase command ids (the settings code stays zh-hans)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:17:06 -04:00
Brandon ThomasandClaude Opus 5.5 313c555b50 LR-BEHAV-LOCALIZE: compile-checked tr_format! for every language catalog
Restore main's compile-checked formatting, generalised to N catalogs: build.rs
writes one tr_format! arm per English message that matches on the active
language and calls format! with that language's translation (English is the
fallback arm). The runtime substitution dropped format specs ({:.1}, {:+},
{:.0}%), e.g. the smart-preview size and the merge progress percentage.

- formats catalogs use main's convention again ({0}/{1} for reordering, {:.0}
  to consume an unused plural suffix); zh-hans converted, with the size and
  percentage placeholders fixed by hand
- build.rs: auto-discovers locales/*-formats.json, validates the code
  (^[a-z]+(-[a-z]+)*$), fails when the catalogs' keys differ, and reruns
  when the locales directory changes
- zh-hans: translate the strings main added since the PR branched
- test: precision, sign and reordered positional values survive in every
  language

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:13:44 -04:00
goujichengandDeepSeek V3.2 22eb4d3515 LR-BEHAV-LOCALIZE: a language table, Simplified Chinese, and per-script font fallback
The localization layer was hardcoded to English and Japanese: an enum with two
variants, an `is_japanese()` gate compiled into the format macro, and one
catalog pair. Adding a language meant editing the menus, the settings row, the
checkmark logic, the format macro and the font stack — each in a different place.

A language is now one entry in `i18n::language_table!`: BCP-47 code, endonym,
ISO 15924 script and catalog. The Language menu, the settings row, the shortcut
sheet and the control channel are all built from that table, and `build.rs`
picks up `locales/<code>-formats.json` by name, so a further language is a table
entry plus two catalogs (docs/localization.md).

Simplified Chinese (`zh-hans`) ships as the second translation: `zh-hans.json`
(1019 entries, key-set and placeholder-identical to `ja.json`) and
`zh-hans-formats.json` (102 entries). Its glyphs come from craft-fonts' Noto
Sans CJK SC, whose `Hans` faces the UI now installs *before* the Japanese ones
for a Chinese reader, so shared Han characters keep the language's forms
(`fonts::cjk_fallback`); the export watermark accepts every CJK face.

`tr_format!` keeps its compile-time check. It formats the English source at the
call site — a runtime template cannot be handed to `format!` — and names those
values so a translation can place them by name (`{n}`) or position (`{_1}`).
Every translation is still checked by `format!`, so a translation with an
unknown placeholder fails the build. The Japanese catalog was rewritten from the
old `{:.0}` plural-discard trick to that by-name form; behaviour is unchanged.

Also: settings persist the BCP-47 code (never the Rust variant name),
`LIGHTCRAFT_LANGUAGE` accepts system-style tags (`zh_CN`, `ja_JP.UTF-8`),
`xtask parity` reads the second command table, and the glyph tests cover every
catalog character plus the interface symbols (`▸`/`▾` turned out to have no
glyph in any installed font and are gone from the Chinese text).

Chinese and Japanese both need `CRAFT_FONTS_DIR` as before; the web build still
embeds only the Japanese face (Noto Sans CJK SC alone would push the wasm past
Cloudflare's 25 MiB per-file cap), documented in docs/localization-zh-hans.md.

Co-Authored-By: DeepSeek V3.2 <noreply@deepseek.com>
2026-10-07 12:22:15 +08:00