Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of [CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md) for the full list. Highlights: client proofing links (#683), custom content pages (#738), asset picker search (#740), concurrent-edit detection (#742), env-locked settings (#739), an accessibility baseline (#741), three security fixes with advisories (#737, #755, #759), and a pre-release quality pass (#780–#797). --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ralksta <ralksta@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ralf <rohlenbo79@gmail.com> Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com> Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com> Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
291 lines
11 KiB
TypeScript
291 lines
11 KiB
TypeScript
/**
|
||
* French dictionary.
|
||
*
|
||
* Typed as `Dictionary` (derived from `en.ts`), so a key added to English but
|
||
* forgotten here fails the type-check rather than falling through to English
|
||
* at runtime.
|
||
*/
|
||
|
||
import type { Dictionary } from '../index';
|
||
|
||
// CLDR: French puts 0 in the singular ("0 photo", "0 jour"), unlike English.
|
||
const isOne = (n: number) => n === 0 || n === 1;
|
||
const plural = (n: number, one: string, other: string) => `${n} ${isOne(n) ? one : other}`;
|
||
|
||
export const fr: Dictionary = {
|
||
dateLocale: 'fr-FR',
|
||
|
||
nav: {
|
||
home: 'Accueil',
|
||
about: 'À propos',
|
||
map: 'Carte',
|
||
journal: 'Journal',
|
||
skipToContent: 'Aller au contenu',
|
||
mainNavAria: 'Navigation principale',
|
||
heroNavAria: 'Rubriques',
|
||
openMenu: 'Ouvrir le menu',
|
||
closeMenu: 'Fermer le menu',
|
||
},
|
||
|
||
common: {
|
||
backTo: (label: string) => `Retour à ${label}`,
|
||
backToGallery: 'Retour à la galerie',
|
||
backToJournal: 'Retour au journal',
|
||
email: 'E-mail',
|
||
website: 'Site web',
|
||
home: 'Accueil',
|
||
photos: (n: number) => plural(n, 'photo', 'photos'),
|
||
albums: (n: number) => plural(n, 'album', 'albums'),
|
||
collections: (n: number) => plural(n, 'collection', 'collections'),
|
||
prevAlbum: 'Album précédent',
|
||
nextAlbum: 'Album suivant',
|
||
prevAlbumAria: (name: string) => `Album précédent : ${name}`,
|
||
nextAlbumAria: (name: string) => `Album suivant : ${name}`,
|
||
albumNavAria: 'Navigation entre les albums',
|
||
prevEntry: 'Article précédent',
|
||
nextEntry: 'Article suivant',
|
||
prevEntryAria: (title: string) => `Article précédent : ${title}`,
|
||
nextEntryAria: (title: string) => `Article suivant : ${title}`,
|
||
entryNavAria: 'Navigation entre les articles du journal',
|
||
loadingGallery: 'Chargement de la galerie',
|
||
gallery: 'Galerie',
|
||
loadingPhotos: 'Chargement des photos',
|
||
downloadAlbum: 'Télécharger l’album',
|
||
},
|
||
|
||
home: {
|
||
enter: 'Entrer',
|
||
pauseSlideshow: 'Mettre le diaporama en pause',
|
||
playSlideshow: 'Lancer le diaporama',
|
||
},
|
||
|
||
error: {
|
||
notFoundTitle: 'Page introuvable',
|
||
notFoundText: 'Cette page n’existe pas, ou l’album n’est plus publié.',
|
||
errorTitle: 'Une erreur est survenue',
|
||
errorText:
|
||
'Cette page n’a pas pu être chargée. C’est généralement temporaire — réessayez dans un instant.',
|
||
tryAgain: 'Réessayer',
|
||
reference: (digest: string) => `Référence : ${digest}`,
|
||
},
|
||
|
||
download: {
|
||
unavailableTitle: 'Téléchargement indisponible',
|
||
notAvailable: 'Ce téléchargement n’est pas disponible.',
|
||
rateLimited: 'Trop de demandes de téléchargement. Patientez un instant, puis réessayez.',
|
||
immichUnavailable:
|
||
'La photothèque est momentanément indisponible. Veuillez réessayer dans un instant.',
|
||
back: 'Retour à la galerie',
|
||
limitReached: 'La limite de téléchargements de ce lien est atteinte.',
|
||
},
|
||
|
||
theme: {
|
||
switchToLight: 'Passer en mode clair',
|
||
switchToDark: 'Passer en mode sombre',
|
||
scrollToTop: 'Revenir en haut',
|
||
},
|
||
|
||
password: {
|
||
subtitle: 'Cette galerie est protégée par un mot de passe.',
|
||
siteSubtitle: 'Ce site est protégé par un mot de passe.',
|
||
pageSubtitle: 'Cette page est protégée par un mot de passe.',
|
||
journalSubtitle: 'Cet article du journal est protégé par un mot de passe.',
|
||
protectedPage: 'Page protégée',
|
||
placeholder: 'Saisissez le mot de passe',
|
||
submit: 'Entrer',
|
||
verifying: 'Vérification…',
|
||
incorrect: 'Mot de passe incorrect. Veuillez réessayer.',
|
||
failed: 'Impossible de vérifier le mot de passe. Veuillez réessayer plus tard.',
|
||
tooManyAttempts: (seconds: number | null) =>
|
||
seconds
|
||
? `Trop de tentatives. Veuillez réessayer dans ${seconds} ${seconds === 1 ? 'seconde' : 'secondes'}.`
|
||
: 'Trop de tentatives. Veuillez réessayer plus tard.',
|
||
},
|
||
|
||
about: {
|
||
kicker: 'À propos',
|
||
title: 'À propos',
|
||
metaTitle: (name: string) => `À propos — ${name}`,
|
||
portraitAlt: 'Portrait',
|
||
gear: 'Matériel',
|
||
},
|
||
|
||
map: {
|
||
title: 'Carte',
|
||
kicker: 'Où',
|
||
subtitle: 'Les lieux où les photos ont été prises',
|
||
loading: 'Chargement de la carte…',
|
||
loadFailed: (status: number) => `Échec du chargement des données de la carte (${status})`,
|
||
initFailed: 'Impossible d’initialiser la carte',
|
||
zoomIn: 'Zoom avant',
|
||
zoomOut: 'Zoom arrière',
|
||
leafletTitle: 'Une bibliothèque JavaScript pour cartes interactives',
|
||
},
|
||
|
||
subpage: {
|
||
collectionKicker: (index: string) => `${index} — Collection`,
|
||
sectionsNav: 'Sections',
|
||
coverAria: (albumName: string, count: string) => `${albumName}, ${count}`,
|
||
nextSubpage: 'Suivant',
|
||
nextSubpageAria: (name: string) => `Suivant : ${name}`,
|
||
},
|
||
|
||
journal: {
|
||
title: 'Journal',
|
||
kicker: 'Récits & essais',
|
||
subtitle: 'Essais photographiques, récits visuels et notes de terrain.',
|
||
description: 'Essais photographiques, récits de voyage et coulisses.',
|
||
entryDescription: 'Article du journal',
|
||
empty: 'Aucun article publié pour le moment.',
|
||
readStory: 'Lire le récit →',
|
||
minRead: (n: number) => `${n} min de lecture`,
|
||
draft: 'Brouillon',
|
||
by: (author: string) => `par ${author}`,
|
||
notFound: 'Introuvable',
|
||
},
|
||
|
||
lightbox: {
|
||
viewer: 'Visionneuse d’images',
|
||
position: (n: number, total: number) => `Photo ${n} sur ${total}`,
|
||
openPhoto: (n: number) => `Afficher la photo ${n}`,
|
||
close: 'Fermer',
|
||
closeTitle: 'Fermer (Échap)',
|
||
previous: 'Photo précédente',
|
||
previousTitle: 'Photo précédente (flèche gauche)',
|
||
next: 'Photo suivante',
|
||
nextTitle: 'Photo suivante (flèche droite)',
|
||
toggleInfo: 'Afficher/masquer les informations',
|
||
toggleInfoTitle: 'Afficher/masquer les informations (i)',
|
||
hideInfo: 'Masquer les infos',
|
||
info: 'Infos',
|
||
loading: 'Chargement…',
|
||
camera: 'Appareil',
|
||
lens: 'Objectif',
|
||
focalLength: 'Focale',
|
||
aperture: 'Ouverture',
|
||
shutter: 'Vitesse',
|
||
iso: 'ISO',
|
||
location: 'Lieu',
|
||
noExif: 'Aucune donnée EXIF disponible',
|
||
copyLink: 'Copier le lien de cette photo',
|
||
copyLinkTitle: 'Copier le lien de cette photo (c)',
|
||
copyLinkShort: 'Lien',
|
||
copied: 'Copié',
|
||
copyManual: 'Copiez ce lien',
|
||
shortcuts: 'Raccourcis clavier',
|
||
shortcutNavigate: 'Photo précédente / suivante',
|
||
shortcutInfo: 'Informations sur la photo',
|
||
shortcutFullscreen: 'Plein écran',
|
||
shortcutExitFullscreen: 'Quitter le plein écran',
|
||
download: 'Télécharger le fichier original',
|
||
downloadTitle: 'Télécharger le fichier original (d)',
|
||
downloadShort: 'Original',
|
||
shortcutDownload: 'Télécharger l’original',
|
||
shortcutSlideshow: 'Diaporama',
|
||
shortcutSlideshowRunning: (seconds: number) => `Diaporama — toutes les ${seconds} s`,
|
||
slideshowStopped: 'Diaporama arrêté',
|
||
slideshowRunning: (seconds: number) =>
|
||
`Diaporama en cours, photo suivante toutes les ${seconds} secondes`,
|
||
shortcutCopyLink: 'Copier le lien de cette photo',
|
||
shortcutList: 'Cette liste',
|
||
shortcutClose: 'Fermer la visionneuse',
|
||
},
|
||
|
||
proofing: {
|
||
addFavorite: 'Ajouter aux favoris',
|
||
removeFavorite: 'Retirer des favoris',
|
||
addToFavorites: 'Ajouter aux favoris',
|
||
removeFromFavorites: 'Retirer des favoris',
|
||
saved: 'Enregistré',
|
||
favorite: 'Favori',
|
||
showAll: 'Tout afficher',
|
||
filterEmpty: 'Aucune photo sélectionnée.',
|
||
selected: (n: number) => `❤️ ${n} ${isOne(n) ? 'sélectionnée' : 'sélectionnées'}`,
|
||
shareExport: 'Partager & exporter',
|
||
modalTitle: (n: number) => `❤️ Sélection (${n})`,
|
||
closeModal: 'Fermer la fenêtre',
|
||
intro: (n: number) =>
|
||
`Vous avez sélectionné ${plural(n, 'photo', 'photos')}. Choisissez une option d’export ci-dessous pour partager votre sélection :`,
|
||
copyLink: 'Copier le lien de partage',
|
||
linkCopied: 'Lien copié !',
|
||
copyList: 'Copier la liste (#1, #2…)',
|
||
listCopied: 'Liste copiée !',
|
||
copyManualLink: 'Copiez ce lien',
|
||
copyManualList: 'Copiez cette liste',
|
||
sendEmail: '✉️ Envoyer un e-mail au photographe',
|
||
downloadSelected: 'Télécharger la sélection (.zip)',
|
||
clearSelection: 'Vider la sélection',
|
||
confirmClear: 'Retirer tous les favoris sélectionnés ?',
|
||
listEmpty: (albumName: string) => `${albumName} : aucune photo sélectionnée.`,
|
||
listSummary: (albumName: string, count: number, indices: string) =>
|
||
`${albumName} — Photos sélectionnées (${count}) : ${indices}`,
|
||
mailSubject: (n: number) => `Sélection de photos (${n})`,
|
||
mailBody: (list: string, url: string) =>
|
||
`Bonjour,\n\nVoici ma sélection de photos :\n\n${list}\n\nLien de partage : ${url}\n\nCordialement,`,
|
||
},
|
||
|
||
proofSession: {
|
||
greeting: (name: string) => `Sélection pour ${name}`,
|
||
intro:
|
||
'Touchez le cœur sur les photos que vous souhaitez. Votre sélection est enregistrée au fur et à mesure — vous pouvez revenir avec ce lien à tout moment.',
|
||
saving: 'Enregistrement…',
|
||
saved: 'Sélection enregistrée',
|
||
saveFailed:
|
||
'Votre sélection n’a pas pu être enregistrée. Vérifiez votre connexion et réessayez.',
|
||
review: 'Vérifier & envoyer',
|
||
modalTitle: (n: number) => `Votre sélection (${n})`,
|
||
empty: 'Aucune photo sélectionnée pour le moment.',
|
||
submit: 'Envoyer la sélection',
|
||
submitting: 'Envoi…',
|
||
confirmSubmit: (n: number) =>
|
||
`Envoyer ${plural(n, 'photo', 'photos')} ? La sélection ne pourra plus être modifiée ensuite.`,
|
||
submitted: 'Merci — votre sélection a été envoyée.',
|
||
locked: 'Cette sélection a été envoyée et ne peut plus être modifiée.',
|
||
validUntil: (date: string) => `Ce lien est valable jusqu’au ${date}.`,
|
||
expiredTitle: 'Ce lien a expiré',
|
||
expiredText: 'Demandez un nouveau lien à votre photographe.',
|
||
downloadSelection: 'Télécharger la sélection (.zip)',
|
||
downloadAll: 'Télécharger toutes les photos (.zip)',
|
||
downloadsLeft: (n: number) =>
|
||
`${plural(n, 'téléchargement restant', 'téléchargements restants')}`,
|
||
},
|
||
|
||
legal: {
|
||
navLabel: 'Mentions légales',
|
||
title: 'Mentions légales',
|
||
subtitle: 'Informations conformément au § 5 DDG',
|
||
address: 'Adresse',
|
||
contact: 'Contact',
|
||
email: 'E-mail',
|
||
phone: 'Téléphone',
|
||
contactForm: 'Formulaire de contact',
|
||
taxSection: 'Informations fiscales',
|
||
taxId: 'Numéro fiscal',
|
||
vatId: 'Numéro de TVA',
|
||
extraInfo: 'Informations complémentaires',
|
||
},
|
||
|
||
contact: {
|
||
navLabel: 'Contact',
|
||
title: 'Contact',
|
||
subtitle: 'Écrivez-moi un message',
|
||
name: 'Nom',
|
||
email: 'E-mail',
|
||
message: 'Message',
|
||
submit: 'Envoyer le message',
|
||
sending: 'Envoi…',
|
||
successTitle: 'Merci',
|
||
successText: 'Votre message est bien arrivé. Je vous répondrai par e-mail.',
|
||
invalid: 'Veuillez remplir tous les champs avec une adresse e-mail valide.',
|
||
tooMany: 'Trop de messages en peu de temps. Réessayez dans une minute.',
|
||
failed: 'Le message n’a pas pu être envoyé. Réessayez plus tard.',
|
||
privacy: (days: number) =>
|
||
`Votre message est conservé sur le serveur de ce site, sert uniquement à vous répondre et est supprimé après ${days} ${isOne(days) ? 'jour' : 'jours'}.`,
|
||
},
|
||
|
||
privacy: {
|
||
navLabel: 'Confidentialité',
|
||
title: 'Politique de confidentialité',
|
||
},
|
||
};
|