- "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>
"Add “{}” to Favorites":"Ajouter « {} » aux favoris",
"Added {n} photo{} to My Photos":"{n} photo(s){} ajoutée(s) à Mes photos",
"Added {n} photo{} to “{}”":"{n} photo(s){} ajoutée(s) à « {} »",
"Added {n} photo{} to My Photos":"{n} photo{} ajoutée(s) à Mes photos",
"Added {n} photo{} to “{}”":"{n} photo{} ajoutée(s) à « {} »",
"Added {what} to {name}":"{what} ajouté à {name}",
"Adding photos… {} of {}":"Ajout des photos… {} sur {}",
"All {n} selected photos move by the same amount.":"Les {n} photos sélectionnées se déplacent de la même valeur.",
"Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh":"Déjà dans la bibliothèque, dans « Supprimés récemment » : {} photo(s){} sélectionnée(s) · clic droit ▸ Restaurer, ou Supprimer définitivement pour importer à nouveau",
"Already in the library: {} photo{} selected in All Photos":"Déjà dans la bibliothèque : {} photo(s){} sélectionnée(s) dans Toutes les photos",
"Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh":"Déjà dans la bibliothèque, dans « Supprimés récemment » : {} photo{} sélectionnée(s) · clic droit → Restaurer, ou Supprimer définitivement pour importer à nouveau",
"Already in the library: {} photo{} selected in All Photos":"Déjà dans la bibliothèque : {} photo{} sélectionnée(s) dans Toutes les photos",
"Move {what} to Recently Deleted?":"Déplacer {what} vers « Supprimés récemment » ?",
"Named ({named_n})":"Nommés ({named_n})",
"Painting “{k}”: click photos to add or remove it · Esc stops":"Application de « {k} » : cliquez sur les photos pour l’ajouter ou le retirer · Échap pour arrêter",
"Paste into {n} photo{} — only settings that were copied are pasted":"Coller dans {n} photo(s){} — seuls les réglages copiés seront collés",
"Paste into {n} photo{} — only settings that were copied are pasted":"Coller dans {n} photo{} — seuls les réglages copiés seront collés",
"Preset: {name}":"Préréglage : {name}",
"Preview only — editing the camera's embedded JPEG ({})":"Aperçu uniquement — modification du JPEG intégré de l’appareil photo ({})",
"Preview only — {}":"Aperçu uniquement — {}",
"Previews ready for {done} photo{plural}":"Aperçus prêts pour {done} photo(s){plural}",
"Previews ready for {done} photo{plural}":"Aperçus prêts pour {done} photo{plural}",
"Profile: {}":"Profil : {}",
"Proof Copy ({})":"Copie d’épreuve ({})",
"Proof Preview · {}":"Aperçu d’épreuve · {}",
@ -96,25 +96,25 @@
"Reading photos… {done} of {total}":"Lecture des photos… {done} sur {total}",
"Reading photos… {} of {}":"Lecture des photos… {} sur {}",
"Redo {}":"Rétablir {}",
"Remove “{name}” and its {count} photo{} from the library?":"Retirer « {name} » et ses {count} photo(s){} de la bibliothèque ?",
"Remove “{name}” and its {count} photo{} from the library?":"Retirer « {name} » et ses {count} photo{} de la bibliothèque ?",
"Remove “{}” from Favorites":"Retirer « {} » des favoris",
"Removed from {}":"Retiré de {}",
"Removed {what} from {name}":"{what} retiré de {name}",
"Rename {n} Photos…":"Renommer {n} photos…",
"Rename “{name}”":"Renommer « {name} »",
"Renames “{from}” on {n} photo{} (keywords below it too). Use | for levels, e.g. Travel|Italy. An existing name merges the two.":"Renomme « {from} » sur {n} photo(s){} (ainsi que les mots-clés qui en dépendent). Utilisez | pour séparer les niveaux, par ex. Voyages|Italie. Si le nom existe déjà, les deux sont fusionnés.",
"Renames “{from}” on {n} photo{} (keywords below it too). Use | for levels, e.g. Travel|Italy. An existing name merges the two.":"Renomme « {from} » sur {n} photo{} (ainsi que les mots-clés qui en dépendent). Utilisez | pour séparer les niveaux, par ex. Voyages|Italie. Si le nom existe déjà, les deux sont fusionnés.",
"Rendering on the CPU: {why}":"Rendu sur le processeur : {why}",
"Scores {scope} for focus and exposure and finds similar shots taken within seconds of each other (bursts).":"Évalue la mise au point et l’exposition de {scope}, puis repère les prises similaires réalisées à quelques secondes d’intervalle (rafales).",
"{f} folders (and their subfolders)":"{f} dossier(s) (et leurs sous-dossiers)",
"{label} Grading":"Étalonnage de {label}",
"{label} {d:+} on every selected photo":"{label} {d:+} sur chaque photo sélectionnée",
@ -144,19 +144,19 @@
"{name} On":"{name} : activé",
"{name} opened for editing; it is stacked with the original":"{name} ouvert pour modification ; il est empilé avec l’original",
"{name} — double-click to reset it":"{name} — double-cliquez pour réinitialiser",
"{n} change{} couldn't be written to disk: {e}\nIf you quit now, {} lost.":"Impossible d’écrire {n} modification(s){} sur le disque : {e}\nSi vous quittez maintenant, {} seront perdues.",
"{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.":"{n} modification(s){} enregistrée(s) en mémoire mais non écrite(s) sur le disque : {e}\nLightCraft réessaiera automatiquement ; quitter maintenant entraînerait la perte de {}.",
"{n} change{} saved in memory but not written to disk: {e} — LightCraft will retry":"{n} modification(s){} enregistrée(s) en mémoire mais non écrite(s) sur le disque : {e} — LightCraft réessaiera.",
"{n} change{} couldn't be written to disk: {e}\nIf you quit now, {} lost.":"Impossible d’écrire {n} modification{} sur le disque : {e}\nSi vous quittez maintenant, {} seront perdues.",
"{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.":"{n} modification{} enregistrée(s) en mémoire mais non écrite(s) sur le disque : {e}\nLightCraft réessaiera automatiquement ; quitter maintenant entraînerait la perte de {}.",
"{n} change{} saved in memory but not written to disk: {e} — LightCraft will retry":"{n} modification{} enregistrée(s) en mémoire mais non écrite(s) sur le disque : {e} — LightCraft réessaiera.",
"{n} found · {sel} selected":"{n} trouvé(s) · {sel} sélectionné(s)",
"{n} photos":"{n} photos",
"{n} photos in {} are hidden by the filters above":"Les filtres ci-dessus masquent {n} photos dans {}",
"{n} photo{}":"{n} photo(s){}",
"{n} photo{} match · updates automatically as photos change":"{n} photo(s){} correspondante(s) · mise à jour automatique lorsque les photos changent",
"{n} photo{} now · updates automatically as photos change":"{n} photo(s){} actuellement · mise à jour automatique lorsque les photos changent",
"{n} photo{}":"{n} photo{}",
"{n} photo{} match · updates automatically as photos change":"{n} photo{} correspondante(s) · mise à jour automatique lorsque les photos changent",
"{n} photo{} now · updates automatically as photos change":"{n} photo{} actuellement · mise à jour automatique lorsque les photos changent",
"{} files and {f} folder{}":"{} fichiers et {f} dossier(s){}",
"{} item{}":"{} élément(s){}",
"{} files and {f} folder{}":"{} fichiers et {f} dossier{}",
"{} item{}":"{} élément{}",
"{} of 4 guides — drag along lines that should be vertical or horizontal.":"{} guides sur 4 — faites-les glisser le long des lignes qui doivent être verticales ou horizontales.",
"{} photo{} not readable":"{} photo(s){} illisible(s)",
"{} sample{}":"{} échantillon(s){}",
"{} photo{} not readable":"{} photo{} illisible(s)",
"{} sample{}":"{} échantillon{}",
"{} sec":"{} s",
"{} spot(s) on this photo":"{} tache(s) sur cette photo",
"{} — show their photos":"{} — afficher leurs photos",
"· {dups} already in the library":"· déjà dans la bibliothèque : {dups}",
"· {n} photo{}":"· {n} photo(s){}",
"· {n} photo{}":"· {n} photo{}",
"… and {more} more":"… et {more} de plus",
"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 ne peut pas encore décoder cette variante RAW ({}). Vous modifiez l’aperçu JPEG intégré de l’appareil photo, qui inclut déjà le style d’image (par ex. monochrome) et la balance des blancs de l’appareil.",
"Filtered: {label}":"Filtré : {label}",
"{n} similar photo{} · View ▸ Clear Filters to see all":"{n} photo(s){} similaire(s) · Affichage ▸ Effacer les filtres pour tout voir",
"{n} similar photo{} · View ▸ Clear Filters to see all":"{n} photo{} similaire(s) · Affichage → Effacer les filtres pour tout voir",
"Moved; {} photo(s) relinked":"Déplacé ; {} photo(s) de nouveau associée(s)",
"{} label (click more labels to show any of them)":"Étiquette {} (cliquez sur « autres étiquettes » pour afficher celles de votre choix)",
", {} star{}":", {} étoile(s){}",
", {} star{}":", {} étoile{}",
", {} label":", étiquette {}",
"Downloads from {host}. Once it has arrived and checked out, LightCraft installs it, {after}.":"Téléchargement depuis {host}. Une fois le téléchargement terminé et vérifié, LightCraft l’installe, {after}.",
"{:.1} MB":"{:.1} Mo",
"Running on the processor: {}.":"Exécution sur le processeur : {}.",
"{} · {}. Kept in the library's “denoise” folder; the oldest ones that no photo uses go first when the limit is reached.":"{} · {}. Conservé dans le dossier « denoise » de la bibliothèque ; lorsque la limite est atteinte, les fichiers les plus anciens inutilisés par les photos sont supprimés en premier.",
"Denoising {name}: {done} of {total} tiles{}":"Réduction du bruit de {name} : {done} sur {total} tuile(s){}",
"Denoising {name}: {done} of {total} tiles{}":"Réduction du bruit de {name} : {done} sur {total} tuile{}",
"{size} GB":"{size} Go",
"{} of {} from {host}":"{} sur {} depuis {host}",
"{} KB":"{} Ko",
"{} MB":"{} Mo",
"Making the denoised picture… {}%":"Création de l’image débruitée… {}%",
"Making the denoised picture… {}%":"Création de l’image débruitée… {}%",
"{name} · {terms}":"{name} · {terms}",
"{n} {noun}":"{n} {noun}",
"Trained on: {provenance}":"Entraîné sur : {provenance}",
"Pending photo action cancelled":"Action de photo en attente annulée",
"Pending photo action cancelled because its library or photo changed":"Action de photo en attente annulée car sa bibliothèque ou sa photo a changé",
"ONNX model":"Modèle ONNX",
"commercial use subject to GPL conditions":"usage commercial soumis aux conditions de la GPL"
"commercial use subject to GPL conditions":"usage commercial soumis aux conditions de la GPL",
"Camera local tone mapping":"Mappage local des tons de l’appareil",
"Apply the local tone mapping stored in the raw (Apple ProRAW), as the camera renders it":"Appliquer le mappage local des tons enregistré dans le fichier raw (Apple ProRAW), comme le rend l’appareil"
The interface ships in the language it is written in (English) plus every language in the table in
`crates/ui-egui/src/i18n.rs`. Today that is English, Simplified Chinese (`zh-hans`), Traditional
Chinese (`zh-hant`, Taiwan), Japanese (`ja`), Brazilian Portuguese (`pt-br`), Spanish (`es`), German (`de`) and Russian (`ru`). This file is the reference for **adding or maintaining a language**; the per-language notes
Chinese (`zh-hant`, Taiwan), Japanese (`ja`), Brazilian Portuguese (`pt-br`), Spanish (`es`), German (`de`), Russian (`ru`) and French (`fr`). This file is the reference for **adding or maintaining a language**; the per-language notes
are in [`localization-zh-hans.md`](localization-zh-hans.md), [`localization-zh-hant.md`](localization-zh-hant.md),
| LR-BEHAV-HEADLESS | Headless UI snapshots | P2 | ✅ | `apps/lightcraft-cli/src/main.rs`, `crates/ui-egui/src/headless.rs`, `docs/control-protocol.md` | `snapshot` rasterizes the UI and defaults photo development to the CPU before the first frame, without adapter discovery; scaled dimensions are validated before allocation, and UI zoom preserves the requested physical viewport; JSON-lines control scripts are supported |
| LR-BEHAV-ACCESS | Accessibility | P2 | 🟡 | `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/panels/grid.rs` | AccessKit (VoiceOver / Narrator / AT-SPI): sliders announce control and value, buttons / icon buttons / dropdowns / section headers / sources their labels and state, grid thumbnails file name, rating, flag and label; the canvas tools (crop, masks) are pointer-only; not audited with a screen reader |
| LR-BEHAV-LOCALE | Language options | P2 | ✅ | `cmd:app.language.english`, `cmd:app.language.simplifiedChinese`, `cmd:app.language.traditionalChinese`, `cmd:app.language.japanese`, `cmd:app.language.portuguese`, `cmd:app.language.spanish`, `cmd:app.language.german`, `cmd:app.language.russian` | Edit > Language lists every language in the table and the active one is checked; shared with Settings > General (docs/localization.md) |
| LR-BEHAV-LOCALE | Language options | P2 | ✅ | `cmd:app.language.english`, `cmd:app.language.simplifiedChinese`, `cmd:app.language.traditionalChinese`, `cmd:app.language.japanese`, `cmd:app.language.portuguese`, `cmd:app.language.french`, `cmd:app.language.spanish`, `cmd:app.language.german`, `cmd:app.language.russian` | Edit > Language lists every language in the table and the active one is checked; shared with Settings > General (docs/localization.md) |
| LR-BEHAV-LOCALIZE | Localisation | P2 | 🟡 | `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/locales/`, `docs/localization.md`, `docs/localization-zh-hans.md`, `docs/localization-zh-hant.md`, `docs/localization-ja.md`, `docs/localization-pt-br.md`, `docs/localization-es.md`, `docs/localization-de.md`, `docs/localization-ru.md` | One table entry per language (code, endonym, ISO 15924 script, catalog): English, Simplified Chinese, Traditional Chinese (Taiwan), Japanese, Brazilian Portuguese, Spanish, German and Russian UI with a persisted language preference and craft-fonts regular/bold faces per script; menus, edit, crop/masks, settings, import/export and primary progress messages. Adding a language is a table entry plus two catalogs. Stock presets/profiles, history steps, library headings, date headings and capture times are localised (user-named items stay verbatim); the German catalog covers every existing catalog key, command/control label and rule-editor label; the other catalogs fall back to English for messages they lack. Technical errors remain English; release notes are translated in German; Traditional Chinese borrows the Simplified Chinese face (no TC face in craft-fonts yet), and the web build embeds the Japanese face in WASM while loading the Simplified Chinese face from a separate font file. |
| LR-BEHAV-LEARN | Tutorials | OOS | 🚫 | | |
| LR-BEHAV-WHATSNEW | What's new | P2 | ✅ | `cmd:app.whatsNew`, `docs/whats-new.md` | Help ▸ What's New: release highlights |