release: v0.19.0

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>
This commit is contained in:
Ralksta
2026-09-29 22:50:30 +02:00
committed by GitHub
parent 5f251923f0
commit ad8b09700b
297 changed files with 19398 additions and 1800 deletions

View File

@@ -19,6 +19,8 @@ export const de: Dictionary = {
map: 'Karte',
journal: 'Journal',
skipToContent: 'Zum Inhalt springen',
mainNavAria: 'Hauptnavigation',
heroNavAria: 'Bereiche',
openMenu: 'Menü öffnen',
closeMenu: 'Menü schließen',
},
@@ -44,12 +46,15 @@ export const de: Dictionary = {
nextEntryAria: (title: string) => `Nächster Beitrag: ${title}`,
entryNavAria: 'Journal-Navigation',
loadingGallery: 'Galerie wird geladen',
gallery: 'Galerie',
loadingPhotos: 'Fotos werden geladen',
downloadAlbum: 'Album herunterladen',
},
home: {
enter: 'Eintreten',
pauseSlideshow: 'Diashow anhalten',
playSlideshow: 'Diashow abspielen',
},
error: {
@@ -58,8 +63,6 @@ export const de: Dictionary = {
errorTitle: 'Etwas ist schiefgelaufen',
errorText:
'Diese Seite konnte gerade nicht geladen werden. Das ist meist vorübergehend — bitte gleich noch einmal versuchen.',
siteErrorText:
'Diese Website konnte gerade nicht geladen werden. Das ist meist vorübergehend — bitte gleich noch einmal versuchen.',
tryAgain: 'Erneut versuchen',
reference: (digest: string) => `Referenz: ${digest}`,
},
@@ -71,6 +74,7 @@ export const de: Dictionary = {
immichUnavailable:
'Die Fotobibliothek ist gerade nicht erreichbar. Bitte gleich noch einmal versuchen.',
back: 'Zurück zur Galerie',
limitReached: 'Das Download-Limit für diesen Link ist erreicht.',
},
theme: {
@@ -82,11 +86,18 @@ export const de: Dictionary = {
password: {
subtitle: 'Diese Galerie ist passwortgeschützt.',
siteSubtitle: 'Diese Website ist passwortgeschützt.',
pageSubtitle: 'Diese Seite ist passwortgeschützt.',
journalSubtitle: 'Dieser Journal-Eintrag ist passwortgeschützt.',
protectedPage: 'Geschützte Seite',
placeholder: 'Passwort eingeben',
submit: 'Öffnen',
verifying: 'Wird geprüft…',
incorrect: 'Falsches Passwort. Bitte erneut versuchen.',
failed: 'Passwort konnte nicht geprüft werden. Bitte später erneut versuchen.',
tooManyAttempts: (seconds: number | null) =>
seconds
? `Zu viele Versuche – bitte in ${seconds} ${seconds === 1 ? 'Sekunde' : 'Sekunden'} erneut versuchen.`
: 'Zu viele Versuche – bitte später erneut versuchen.',
},
about: {
@@ -104,6 +115,9 @@ export const de: Dictionary = {
loading: 'Karte wird geladen…',
loadFailed: (status: number) => `Kartendaten konnten nicht geladen werden (${status})`,
initFailed: 'Karte konnte nicht initialisiert werden',
zoomIn: 'Vergrößern',
zoomOut: 'Verkleinern',
leafletTitle: 'Eine JavaScript-Bibliothek für interaktive Karten',
},
subpage: {
@@ -130,6 +144,7 @@ export const de: Dictionary = {
lightbox: {
viewer: 'Bildansicht',
position: (n: number, total: number) => `Foto ${n} von ${total}`,
openPhoto: (n: number) => `Foto ${n} ansehen`,
close: 'Schließen',
closeTitle: 'Schließen (Esc)',
@@ -164,10 +179,10 @@ export const de: Dictionary = {
downloadTitle: 'Originaldatei herunterladen (d)',
downloadShort: 'Original',
shortcutDownload: 'Original herunterladen',
shortcutSlideshow: 'Diaschau',
shortcutSlideshowRunning: (seconds: number) => `Diaschau — alle ${seconds}s`,
slideshowStopped: 'Diaschau angehalten',
slideshowRunning: (seconds: number) => `Diaschau läuft, wechselt alle ${seconds} Sekunden`,
shortcutSlideshow: 'Diashow',
shortcutSlideshowRunning: (seconds: number) => `Diashow — alle ${seconds}s`,
slideshowStopped: 'Diashow angehalten',
slideshowRunning: (seconds: number) => `Diashow läuft, wechselt alle ${seconds} Sekunden`,
shortcutCopyLink: 'Link zum Foto kopieren',
shortcutList: 'Diese Liste',
shortcutClose: 'Ansicht schließen',
@@ -181,6 +196,7 @@ export const de: Dictionary = {
saved: 'Gemerkt',
favorite: 'Merken',
showAll: 'Alle anzeigen',
filterEmpty: 'Keine Fotos ausgewählt.',
selected: (n: number) => `❤️ ${n} ausgewählt`,
shareExport: 'Teilen & Export',
modalTitle: (n: number) => `❤️ Auswahl (${n})`,
@@ -200,11 +216,36 @@ export const de: Dictionary = {
listEmpty: (albumName: string) => `${albumName}: Keine Fotos ausgewählt.`,
listSummary: (albumName: string, count: number, indices: string) =>
`${albumName} — Ausgewählte Fotos (${count}): ${indices}`,
mailSubject: (n: number) => `Fotoauswahl (${n} Fotos)`,
mailSubject: (n: number) => `Fotoauswahl (${plural(n, 'Foto', 'Fotos')})`,
mailBody: (list: string, url: string) =>
`Hallo,\n\nhier ist meine Fotoauswahl:\n\n${list}\n\nLink zur Auswahl: ${url}\n\nViele Grüße,`,
},
proofSession: {
greeting: (name: string) => `Auswahl für ${name}`,
intro:
'Markiere die gewünschten Fotos mit dem Herz. Deine Auswahl wird laufend gespeichert — du kannst mit diesem Link jederzeit zurückkommen.',
saving: 'Wird gespeichert…',
saved: 'Auswahl gespeichert',
saveFailed:
'Deine Auswahl konnte nicht gespeichert werden. Prüfe die Verbindung und versuche es erneut.',
review: 'Prüfen & absenden',
modalTitle: (n: number) => `Deine Auswahl (${n})`,
empty: 'Noch keine Fotos ausgewählt.',
submit: 'Auswahl absenden',
submitting: 'Wird gesendet…',
confirmSubmit: (n: number) =>
`${plural(n, 'Foto', 'Fotos')} absenden? Danach lässt sich die Auswahl nicht mehr ändern.`,
submitted: 'Danke — deine Auswahl wurde abgeschickt.',
locked: 'Diese Auswahl wurde abgeschickt und kann nicht mehr geändert werden.',
validUntil: (date: string) => `Dieser Link ist gültig bis ${date}.`,
expiredTitle: 'Dieser Link ist abgelaufen',
expiredText: 'Bitte frag deinen Fotografen nach einem neuen Link.',
downloadSelection: 'Auswahl herunterladen (.zip)',
downloadAll: 'Alle Fotos herunterladen (.zip)',
downloadsLeft: (n: number) => `Noch ${plural(n, 'Download', 'Downloads')}`,
},
legal: {
navLabel: 'Impressum',
title: 'Impressum',

View File

@@ -19,6 +19,8 @@ export const en = {
map: 'Map',
journal: 'Journal',
skipToContent: 'Skip to content',
mainNavAria: 'Main',
heroNavAria: 'Sections',
openMenu: 'Open menu',
closeMenu: 'Close menu',
},
@@ -45,12 +47,15 @@ export const en = {
nextEntryAria: (title: string) => `Next entry: ${title}`,
entryNavAria: 'Journal entry navigation',
loadingGallery: 'Loading gallery',
gallery: 'Gallery',
loadingPhotos: 'Loading photos',
downloadAlbum: 'Download album',
},
home: {
enter: 'Enter',
pauseSlideshow: 'Pause slideshow',
playSlideshow: 'Play slideshow',
},
error: {
@@ -59,8 +64,6 @@ export const en = {
errorTitle: 'Something went wrong',
errorText:
'This page could not be loaded right now. It is usually temporary — try again in a moment.',
siteErrorText:
'This site could not be loaded right now. It is usually temporary — try again in a moment.',
tryAgain: 'Try again',
reference: (digest: string) => `Reference: ${digest}`,
},
@@ -72,6 +75,7 @@ export const en = {
rateLimited: 'Too many download requests. Please wait a moment and try again.',
immichUnavailable: 'The photo library is unavailable right now. Please try again shortly.',
back: 'Back to the gallery',
limitReached: 'The download limit for this link has been reached.',
},
theme: {
@@ -83,11 +87,18 @@ export const en = {
password: {
subtitle: 'This gallery is password-protected.',
siteSubtitle: 'This site is password-protected.',
pageSubtitle: 'This page is password-protected.',
journalSubtitle: 'This journal entry is password-protected.',
protectedPage: 'Protected page',
placeholder: 'Enter password',
submit: 'Enter',
verifying: 'Verifying…',
incorrect: 'Incorrect password. Please try again.',
failed: 'Unable to verify password. Please try again later.',
tooManyAttempts: (seconds: number | null) =>
seconds
? `Too many attempts. Please try again in ${seconds} ${seconds === 1 ? 'second' : 'seconds'}.`
: 'Too many attempts. Please try again later.',
},
about: {
@@ -105,6 +116,10 @@ export const en = {
loading: 'Loading map…',
loadFailed: (status: number) => `Failed to load map data (${status})`,
initFailed: 'Failed to initialize map',
zoomIn: 'Zoom in',
zoomOut: 'Zoom out',
// The title on the Leaflet credit in the map's corner.
leafletTitle: 'A JavaScript library for interactive maps',
},
subpage: {
@@ -131,6 +146,7 @@ export const en = {
lightbox: {
viewer: 'Image viewer',
position: (n: number, total: number) => `Photo ${n} of ${total}`,
/** A grid tile, which opens the viewer on that photo. */
openPhoto: (n: number) => `View photo ${n}`,
close: 'Close',
@@ -183,6 +199,8 @@ export const en = {
saved: 'Saved',
favorite: 'Favorite',
showAll: 'Show All',
/** Shown in place of the grid when the filter is on and nothing is selected. */
filterEmpty: 'No photos selected.',
selected: (n: number) => `❤️ ${n} Selected`,
shareExport: 'Share & Export',
modalTitle: (n: number) => `❤️ Selection (${n})`,
@@ -203,11 +221,36 @@ export const en = {
listEmpty: (albumName: string) => `${albumName}: No photos selected.`,
listSummary: (albumName: string, count: number, indices: string) =>
`${albumName} — Selected Photos (${count}): ${indices}`,
mailSubject: (n: number) => `Photo Selection (${n} items)`,
mailSubject: (n: number) => `Photo Selection (${plural(n, 'item', 'items')})`,
mailBody: (list: string, url: string) =>
`Hello,\n\nHere is my photo selection:\n\n${list}\n\nShare Link: ${url}\n\nBest regards,`,
},
/** Client proofing links (/proof/<token>). */
proofSession: {
greeting: (name: string) => `Selection for ${name}`,
intro:
'Tap the heart on the photos you want. Your selection is saved as you go — you can come back with this link at any time.',
saving: 'Saving…',
saved: 'Selection saved',
saveFailed: 'Your selection could not be saved. Check your connection and try again.',
review: 'Review & submit',
modalTitle: (n: number) => `Your selection (${n})`,
empty: 'No photos selected yet.',
submit: 'Submit selection',
submitting: 'Submitting…',
confirmSubmit: (n: number) =>
`Submit ${plural(n, 'photo', 'photos')}? The selection cannot be changed afterwards.`,
submitted: 'Thank you — your selection has been submitted.',
locked: 'This selection has been submitted and can no longer be changed.',
validUntil: (date: string) => `This link is valid until ${date}.`,
expiredTitle: 'This link has expired',
expiredText: 'Please ask your photographer for a new link.',
downloadSelection: 'Download selection (.zip)',
downloadAll: 'Download all photos (.zip)',
downloadsLeft: (n: number) => `${plural(n, 'download', 'downloads')} left`,
},
legal: {
navLabel: 'Legal Notice',
title: 'Legal Notice',

View File

@@ -19,6 +19,8 @@ export const es: Dictionary = {
map: 'Mapa',
journal: 'Diario',
skipToContent: 'Saltar al contenido',
mainNavAria: 'Navegación principal',
heroNavAria: 'Secciones',
openMenu: 'Abrir menú',
closeMenu: 'Cerrar menú',
},
@@ -44,12 +46,15 @@ export const es: Dictionary = {
nextEntryAria: (title: string) => `Entrada siguiente: ${title}`,
entryNavAria: 'Navegación entre entradas del diario',
loadingGallery: 'Cargando la galería',
gallery: 'Galería',
loadingPhotos: 'Cargando fotos',
downloadAlbum: 'Descargar álbum',
},
home: {
enter: 'Entrar',
pauseSlideshow: 'Pausar la presentación',
playSlideshow: 'Reproducir la presentación',
},
error: {
@@ -58,8 +63,6 @@ export const es: Dictionary = {
errorTitle: 'Algo ha salido mal',
errorText:
'No se ha podido cargar esta página. Suele ser algo temporal: inténtalo de nuevo en un momento.',
siteErrorText:
'No se ha podido cargar este sitio. Suele ser algo temporal: inténtalo de nuevo en un momento.',
tryAgain: 'Reintentar',
reference: (digest: string) => `Referencia: ${digest}`,
},
@@ -71,6 +74,7 @@ export const es: Dictionary = {
immichUnavailable:
'La fototeca no está disponible en este momento. Vuelve a intentarlo en breve.',
back: 'Volver a la galería',
limitReached: 'Se ha alcanzado el límite de descargas de este enlace.',
},
theme: {
@@ -82,11 +86,18 @@ export const es: Dictionary = {
password: {
subtitle: 'Esta galería está protegida con contraseña.',
siteSubtitle: 'Este sitio está protegido con contraseña.',
pageSubtitle: 'Esta página está protegida con contraseña.',
journalSubtitle: 'Esta entrada del diario está protegida con contraseña.',
protectedPage: 'Página protegida',
placeholder: 'Introduce la contraseña',
submit: 'Entrar',
verifying: 'Verificando…',
incorrect: 'Contraseña incorrecta. Inténtalo de nuevo.',
failed: 'No se ha podido verificar la contraseña. Inténtalo más tarde.',
tooManyAttempts: (seconds: number | null) =>
seconds
? `Demasiados intentos. Inténtalo de nuevo en ${seconds} ${seconds === 1 ? 'segundo' : 'segundos'}.`
: 'Demasiados intentos. Inténtalo más tarde.',
},
about: {
@@ -104,6 +115,9 @@ export const es: Dictionary = {
loading: 'Cargando el mapa…',
loadFailed: (status: number) => `No se pudieron cargar los datos del mapa (${status})`,
initFailed: 'No se pudo inicializar el mapa',
zoomIn: 'Acercar',
zoomOut: 'Alejar',
leafletTitle: 'Una biblioteca de JavaScript para mapas interactivos',
},
subpage: {
@@ -130,6 +144,7 @@ export const es: Dictionary = {
lightbox: {
viewer: 'Visor de imágenes',
position: (n: number, total: number) => `Foto ${n} de ${total}`,
openPhoto: (n: number) => `Ver foto ${n}`,
close: 'Cerrar',
closeTitle: 'Cerrar (Esc)',
@@ -181,6 +196,7 @@ export const es: Dictionary = {
saved: 'Guardado',
favorite: 'Favorito',
showAll: 'Mostrar todo',
filterEmpty: 'No hay fotos seleccionadas.',
selected: (n: number) => `❤️ ${n} ${n === 1 ? 'seleccionada' : 'seleccionadas'}`,
shareExport: 'Compartir y exportar',
modalTitle: (n: number) => `❤️ Selección (${n})`,
@@ -205,6 +221,30 @@ export const es: Dictionary = {
`Hola:\n\nEsta es mi selección de fotos:\n\n${list}\n\nEnlace: ${url}\n\nUn saludo,`,
},
proofSession: {
greeting: (name: string) => `Selección para ${name}`,
intro:
'Toca el corazón en las fotos que quieras. Tu selección se guarda sobre la marcha: puedes volver con este enlace cuando quieras.',
saving: 'Guardando…',
saved: 'Selección guardada',
saveFailed: 'No se ha podido guardar tu selección. Revisa la conexión e inténtalo de nuevo.',
review: 'Revisar y enviar',
modalTitle: (n: number) => `Tu selección (${n})`,
empty: 'Todavía no has seleccionado ninguna foto.',
submit: 'Enviar selección',
submitting: 'Enviando…',
confirmSubmit: (n: number) =>
`¿Enviar ${plural(n, 'foto', 'fotos')}? Después no podrás cambiar la selección.`,
submitted: 'Gracias: tu selección se ha enviado.',
locked: 'Esta selección ya se ha enviado y no se puede modificar.',
validUntil: (date: string) => `Este enlace es válido hasta el ${date}.`,
expiredTitle: 'Este enlace ha caducado',
expiredText: 'Pide un enlace nuevo a tu fotógrafo.',
downloadSelection: 'Descargar selección (.zip)',
downloadAll: 'Descargar todas las fotos (.zip)',
downloadsLeft: (n: number) => `${plural(n, 'descarga restante', 'descargas restantes')}`,
},
legal: {
navLabel: 'Aviso legal',
title: 'Aviso legal',

View File

@@ -8,7 +8,9 @@
import type { Dictionary } from '../index';
const plural = (n: number, one: string, other: string) => `${n} ${n === 1 ? one : other}`;
// 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',
@@ -19,6 +21,8 @@ export const fr: Dictionary = {
map: 'Carte',
journal: 'Journal',
skipToContent: 'Aller au contenu',
mainNavAria: 'Navigation principale',
heroNavAria: 'Rubriques',
openMenu: 'Ouvrir le menu',
closeMenu: 'Fermer le menu',
},
@@ -44,12 +48,15 @@ export const fr: Dictionary = {
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: {
@@ -58,8 +65,6 @@ export const fr: Dictionary = {
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.',
siteErrorText:
'Ce site n’a pas pu être chargé. C’est généralement temporaire — réessayez dans un instant.',
tryAgain: 'Réessayer',
reference: (digest: string) => `Référence : ${digest}`,
},
@@ -71,6 +76,7 @@ export const fr: Dictionary = {
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: {
@@ -82,11 +88,18 @@ export const fr: Dictionary = {
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: {
@@ -104,6 +117,9 @@ export const fr: Dictionary = {
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: {
@@ -130,6 +146,7 @@ export const fr: Dictionary = {
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)',
@@ -182,7 +199,8 @@ export const fr: Dictionary = {
saved: 'Enregistré',
favorite: 'Favori',
showAll: 'Tout afficher',
selected: (n: number) => `❤️ ${n} ${n === 1 ? 'sélectionnée' : 'sélectionnées'}`,
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',
@@ -206,6 +224,32 @@ export const fr: Dictionary = {
`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',
@@ -236,7 +280,7 @@ export const fr: Dictionary = {
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} ${days === 1 ? 'jour' : 'jours'}.`,
`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: {

View File

@@ -19,6 +19,8 @@ export const it: Dictionary = {
map: 'Mappa',
journal: 'Diario',
skipToContent: 'Vai al contenuto',
mainNavAria: 'Navigazione principale',
heroNavAria: 'Sezioni',
openMenu: 'Apri menu',
closeMenu: 'Chiudi menu',
},
@@ -45,12 +47,15 @@ export const it: Dictionary = {
nextEntryAria: (title: string) => `Articolo successivo: ${title}`,
entryNavAria: 'Navigazione tra gli articoli del diario',
loadingGallery: 'Caricamento della galleria',
gallery: 'Galleria',
loadingPhotos: 'Caricamento delle foto',
downloadAlbum: 'Scarica album',
},
home: {
enter: 'Entra',
pauseSlideshow: 'Metti in pausa la presentazione',
playSlideshow: 'Avvia la presentazione',
},
error: {
@@ -59,8 +64,6 @@ export const it: Dictionary = {
errorTitle: 'Qualcosa è andato storto',
errorText:
'Non è stato possibile caricare questa pagina. Di solito è temporaneo: riprova tra un momento.',
siteErrorText:
'Non è stato possibile caricare questo sito. Di solito è temporaneo: riprova tra un momento.',
tryAgain: 'Riprova',
reference: (digest: string) => `Riferimento: ${digest}`,
},
@@ -71,6 +74,7 @@ export const it: Dictionary = {
rateLimited: 'Troppe richieste di download. Attendi un momento e riprova.',
immichUnavailable: 'La libreria fotografica non è al momento disponibile. Riprova a breve.',
back: 'Torna alla galleria',
limitReached: 'È stato raggiunto il limite di download per questo link.',
},
theme: {
@@ -82,11 +86,18 @@ export const it: Dictionary = {
password: {
subtitle: 'Questa galleria è protetta da password.',
siteSubtitle: 'Questo sito è protetto da password.',
pageSubtitle: 'Questa pagina è protetta da password.',
journalSubtitle: 'Questo articolo del diario è protetto da password.',
protectedPage: 'Pagina protetta',
placeholder: 'Inserisci la password',
submit: 'Entra',
verifying: 'Verifica in corso…',
incorrect: 'Password errata. Riprova.',
failed: 'Impossibile verificare la password. Riprova più tardi.',
tooManyAttempts: (seconds: number | null) =>
seconds
? `Troppi tentativi. Riprova tra ${seconds} ${seconds === 1 ? 'secondo' : 'secondi'}.`
: 'Troppi tentativi. Riprova più tardi.',
},
about: {
@@ -104,6 +115,9 @@ export const it: Dictionary = {
loading: 'Caricamento della mappa…',
loadFailed: (status: number) => `Impossibile caricare i dati della mappa (${status})`,
initFailed: 'Impossibile inizializzare la mappa',
zoomIn: 'Ingrandisci',
zoomOut: 'Riduci',
leafletTitle: 'Una libreria JavaScript per mappe interattive',
},
subpage: {
@@ -130,6 +144,7 @@ export const it: Dictionary = {
lightbox: {
viewer: 'Visualizzatore di immagini',
position: (n: number, total: number) => `Foto ${n} di ${total}`,
openPhoto: (n: number) => `Apri la foto ${n}`,
close: 'Chiudi',
closeTitle: 'Chiudi (Esc)',
@@ -181,6 +196,7 @@ export const it: Dictionary = {
saved: 'Salvato',
favorite: 'Preferito',
showAll: 'Mostra tutto',
filterEmpty: 'Nessuna foto selezionata.',
selected: (n: number) => `❤️ ${n} ${n === 1 ? 'selezionata' : 'selezionate'}`,
shareExport: 'Condividi ed esporta',
modalTitle: (n: number) => `❤️ Selezione (${n})`,
@@ -205,6 +221,30 @@ export const it: Dictionary = {
`Ciao,\n\necco la mia selezione di foto:\n\n${list}\n\nLink di condivisione: ${url}\n\nCordiali saluti,`,
},
proofSession: {
greeting: (name: string) => `Selezione per ${name}`,
intro:
'Tocca il cuore sulle foto che desideri. La tua selezione viene salvata man mano: puoi tornare con questo link in qualsiasi momento.',
saving: 'Salvataggio…',
saved: 'Selezione salvata',
saveFailed: 'Non è stato possibile salvare la selezione. Controlla la connessione e riprova.',
review: 'Controlla e invia',
modalTitle: (n: number) => `La tua selezione (${n})`,
empty: 'Nessuna foto selezionata.',
submit: 'Invia selezione',
submitting: 'Invio in corso…',
confirmSubmit: (n: number) =>
`Inviare ${n} foto? Dopo non sarà più possibile modificare la selezione.`,
submitted: 'Grazie, la tua selezione è stata inviata.',
locked: 'Questa selezione è stata inviata e non può più essere modificata.',
validUntil: (date: string) => `Questo link è valido fino al ${date}.`,
expiredTitle: 'Questo link è scaduto',
expiredText: 'Chiedi un nuovo link al tuo fotografo.',
downloadSelection: 'Scarica selezione (.zip)',
downloadAll: 'Scarica tutte le foto (.zip)',
downloadsLeft: (n: number) => `${plural(n, 'download rimasto', 'download rimasti')}`,
},
legal: {
navLabel: 'Note legali',
title: 'Note legali',

View File

@@ -19,6 +19,8 @@ export const nl: Dictionary = {
map: 'Kaart',
journal: 'Journal',
skipToContent: 'Naar de inhoud',
mainNavAria: 'Hoofdnavigatie',
heroNavAria: 'Onderdelen',
openMenu: 'Menu openen',
closeMenu: 'Menu sluiten',
},
@@ -44,12 +46,15 @@ export const nl: Dictionary = {
nextEntryAria: (title: string) => `Volgend verhaal: ${title}`,
entryNavAria: 'Navigatie tussen journalverhalen',
loadingGallery: 'Galerij laden',
gallery: 'Galerie',
loadingPhotos: 'Foto’s laden',
downloadAlbum: 'Album downloaden',
},
home: {
enter: 'Binnenkomen',
pauseSlideshow: 'Diavoorstelling pauzeren',
playSlideshow: 'Diavoorstelling afspelen',
},
error: {
@@ -58,8 +63,6 @@ export const nl: Dictionary = {
errorTitle: 'Er is iets misgegaan',
errorText:
'Deze pagina kon niet worden geladen. Meestal is dat tijdelijk — probeer het zo nog eens.',
siteErrorText:
'Deze site kon niet worden geladen. Meestal is dat tijdelijk — probeer het zo nog eens.',
tryAgain: 'Opnieuw proberen',
reference: (digest: string) => `Referentie: ${digest}`,
},
@@ -71,6 +74,7 @@ export const nl: Dictionary = {
immichUnavailable:
'De fotobibliotheek is op dit moment niet bereikbaar. Probeer het zo opnieuw.',
back: 'Terug naar de galerij',
limitReached: 'Het downloadlimiet voor deze link is bereikt.',
},
theme: {
@@ -82,11 +86,18 @@ export const nl: Dictionary = {
password: {
subtitle: 'Deze galerij is met een wachtwoord beveiligd.',
siteSubtitle: 'Deze site is met een wachtwoord beveiligd.',
pageSubtitle: 'Deze pagina is met een wachtwoord beveiligd.',
journalSubtitle: 'Dit journalverhaal is met een wachtwoord beveiligd.',
protectedPage: 'Beveiligde pagina',
placeholder: 'Voer het wachtwoord in',
submit: 'Binnenkomen',
verifying: 'Controleren…',
incorrect: 'Onjuist wachtwoord. Probeer het opnieuw.',
failed: 'Het wachtwoord kon niet worden gecontroleerd. Probeer het later opnieuw.',
tooManyAttempts: (seconds: number | null) =>
seconds
? `Te veel pogingen. Probeer het over ${seconds} ${seconds === 1 ? 'seconde' : 'seconden'} opnieuw.`
: 'Te veel pogingen. Probeer het later opnieuw.',
},
about: {
@@ -104,6 +115,9 @@ export const nl: Dictionary = {
loading: 'Kaart laden…',
loadFailed: (status: number) => `Kaartgegevens konden niet worden geladen (${status})`,
initFailed: 'De kaart kon niet worden gestart',
zoomIn: 'Inzoomen',
zoomOut: 'Uitzoomen',
leafletTitle: 'Een JavaScript-bibliotheek voor interactieve kaarten',
},
subpage: {
@@ -130,6 +144,7 @@ export const nl: Dictionary = {
lightbox: {
viewer: 'Fotoviewer',
position: (n: number, total: number) => `Foto ${n} van ${total}`,
openPhoto: (n: number) => `Foto ${n} bekijken`,
close: 'Sluiten',
closeTitle: 'Sluiten (Esc)',
@@ -182,6 +197,7 @@ export const nl: Dictionary = {
saved: 'Opgeslagen',
favorite: 'Favoriet',
showAll: 'Alles tonen',
filterEmpty: "Geen foto's geselecteerd.",
selected: (n: number) => `❤️ ${n} geselecteerd`,
shareExport: 'Delen & exporteren',
modalTitle: (n: number) => `❤️ Selectie (${n})`,
@@ -206,6 +222,31 @@ export const nl: Dictionary = {
`Hallo,\n\nHier is mijn fotoselectie:\n\n${list}\n\nDeellink: ${url}\n\nMet vriendelijke groet,`,
},
proofSession: {
greeting: (name: string) => `Selectie voor ${name}`,
intro:
'Tik op het hartje bij de foto’s die je wilt. Je selectie wordt meteen opgeslagen — je kunt met deze link altijd terugkomen.',
saving: 'Opslaan…',
saved: 'Selectie opgeslagen',
saveFailed:
'Je selectie kon niet worden opgeslagen. Controleer je verbinding en probeer het opnieuw.',
review: 'Bekijken & versturen',
modalTitle: (n: number) => `Jouw selectie (${n})`,
empty: 'Nog geen foto’s geselecteerd.',
submit: 'Selectie versturen',
submitting: 'Versturen…',
confirmSubmit: (n: number) =>
`${plural(n, 'foto', 'foto’s')} versturen? Daarna kun je de selectie niet meer wijzigen.`,
submitted: 'Bedankt — je selectie is verstuurd.',
locked: 'Deze selectie is verstuurd en kan niet meer worden gewijzigd.',
validUntil: (date: string) => `Deze link is geldig tot ${date}.`,
expiredTitle: 'Deze link is verlopen',
expiredText: 'Vraag je fotograaf om een nieuwe link.',
downloadSelection: 'Selectie downloaden (.zip)',
downloadAll: 'Alle foto’s downloaden (.zip)',
downloadsLeft: (n: number) => `Nog ${plural(n, 'download', 'downloads')}`,
},
legal: {
navLabel: 'Colofon',
title: 'Colofon',