From 898a41301d6244819a9c1e0a737976612acbcaf9 Mon Sep 17 00:00:00 2001 From: Benjy Date: Fri, 25 Sep 2026 11:23:21 +0200 Subject: [PATCH] Show the trash, and let an administrator set what it keeps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The trash has been here since its engine landed: deleting moves an item into the volume's reserved space, it is restorable, it is swept on a schedule, and an administrator can configure a retention. None of it was visible. There was no way to see what had been deleted, no way to put something back, and the settings could be written only through the API — so the feature existed and nobody could use it. The screen lists what the signed-in person deleted, newest first, with where it came from, when it went and what it costs; a deleted folder opens, so a single file can be taken back out of it without restoring the whole thing. Selecting restores, or deletes for good, and emptying asks first. An item whose volume is not mounted any more is shown as unreachable rather than hidden, with the reason, because a trash that quietly drops things is worse than one that says it cannot reach them. Share links that were suspended with the item are restored or dropped, which the dialog asks about when there are any. Settings, for an administrator: whether deleting goes to the trash at all, how long it keeps things, what share of the volume it may take, and the same for the versions kept beside it — the server has been answering both sections since they landed, and the settings store now carries them. Left out on purpose, with the batches that bring what they need: reading the text of a file while it is still in the trash (it wants the text service), and restoring into a chosen folder rather than where the item came from (it wants the destination dialog). Restoring puts an item back where it was deleted from, without replacing anything that has taken its name since. --- frontend/src/api/index.js | 1 + frontend/src/api/trash.api.js | 93 ++ frontend/src/components/TrashContextMenu.vue | 177 +++ frontend/src/components/TrashMenu.vue | 38 + frontend/src/i18n/locales/de.json | 171 ++- frontend/src/i18n/locales/en.json | 171 ++- frontend/src/i18n/locales/es.json | 171 ++- frontend/src/i18n/locales/fr.json | 171 ++- frontend/src/i18n/locales/hi.json | 171 ++- frontend/src/i18n/locales/it.json | 171 ++- frontend/src/i18n/locales/ko.json | 171 ++- frontend/src/i18n/locales/nl.json | 171 ++- frontend/src/i18n/locales/pl.json | 171 ++- frontend/src/i18n/locales/pt-BR.json | 171 ++- frontend/src/i18n/locales/ro.json | 171 ++- frontend/src/i18n/locales/ru.json | 171 ++- frontend/src/i18n/locales/sv.json | 171 ++- frontend/src/i18n/locales/zh-CN.json | 171 ++- frontend/src/i18n/locales/zh-TW.json | 171 ++- frontend/src/layouts/BrowserLayout.vue | 2 + frontend/src/router/index.js | 13 + frontend/src/stores/appSettings.js | 11 + frontend/src/stores/features.js | 8 + frontend/src/utils/folderRoute.js | 38 + frontend/src/utils/index.js | 10 +- frontend/src/views/TrashView.vue | 1137 +++++++++++++++++ frontend/src/views/settings/SettingsTrash.vue | 565 ++++++++ frontend/src/views/settings/SettingsView.vue | 7 + 28 files changed, 4649 insertions(+), 16 deletions(-) create mode 100644 frontend/src/api/trash.api.js create mode 100644 frontend/src/components/TrashContextMenu.vue create mode 100644 frontend/src/components/TrashMenu.vue create mode 100644 frontend/src/utils/folderRoute.js create mode 100644 frontend/src/views/TrashView.vue create mode 100644 frontend/src/views/settings/SettingsTrash.vue diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index e8622861..d3295f11 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -14,3 +14,4 @@ export * from './onlyoffice.api'; export * from './collabora.api'; export * from './features.api'; export * from './terminal.api'; +export * from './trash.api'; diff --git a/frontend/src/api/trash.api.js b/frontend/src/api/trash.api.js new file mode 100644 index 00000000..87259ab2 --- /dev/null +++ b/frontend/src/api/trash.api.js @@ -0,0 +1,93 @@ +import { requestJson, requestStream } from './http'; + +/** + * The trash: what the signed-in person can see in it, and what they can do + * with it. The server decides visibility and who may restore where; these only + * carry the request. + */ + +const post = (endpoint, body = {}) => + requestJson(endpoint, { method: 'POST', body: JSON.stringify(body) }); + +async function getTrash() { + return requestJson('/api/trash', { method: 'GET' }); +} + +/** `shares`: what becomes of the share links the items kept — 'restore' or 'drop'. */ +async function restoreTrashItems(ids, { shares } = {}) { + return post('/api/trash/restore', shares ? { ids, shares } : { ids }); +} + +/** What a deleted folder holds, at `entryPath` inside it ('' for its top). */ +async function getTrashEntries(id, entryPath = '') { + const query = entryPath ? `?path=${encodeURIComponent(entryPath)}` : ''; + return requestJson(`/api/trash/items/${encodeURIComponent(id)}/entries${query}`, { + method: 'GET', + }); +} + +/** Put back entries from inside a deleted folder; the rest of it stays in the trash. */ +async function restoreTrashEntries(id, paths, { shares } = {}) { + return post( + `/api/trash/items/${encodeURIComponent(id)}/restore`, + shares ? { paths, shares } : { paths } + ); +} + +/** + * Put items in a chosen folder. Streamed like a transfer, since across disks it + * is a copy: `onEvent` receives start and progress, and the result is the final + * `{ destination, items }`. + */ +async function restoreTrashItemsTo(ids, destination, { onEvent, signal, shares } = {}) { + return requestStream('/api/trash/restore-to', { + method: 'POST', + body: JSON.stringify(shares ? { ids, destination, shares } : { ids, destination }), + onEvent, + signal, + }); +} + +/** Put entries of a deleted folder in a chosen folder, streamed the same way. */ +async function restoreTrashEntriesTo(id, paths, destination, { onEvent, signal, shares } = {}) { + return requestStream(`/api/trash/items/${encodeURIComponent(id)}/restore-to`, { + method: 'POST', + body: JSON.stringify(shares ? { paths, destination, shares } : { paths, destination }), + onEvent, + signal, + }); +} + +async function deleteTrashItems(ids, { forgetUnavailable = false } = {}) { + return post('/api/trash/delete', forgetUnavailable ? { ids, forgetUnavailable: true } : { ids }); +} + +async function emptyTrash() { + return post('/api/trash/empty'); +} + +async function getTrashZones() { + return requestJson('/api/trash/zones', { method: 'GET' }); +} + +async function verifyTrash() { + return post('/api/trash/verify'); +} + +async function runTrashMaintenance() { + return post('/api/trash/maintenance'); +} + +export { + getTrash, + getTrashEntries, + restoreTrashItems, + restoreTrashEntries, + restoreTrashItemsTo, + restoreTrashEntriesTo, + deleteTrashItems, + emptyTrash, + getTrashZones, + verifyTrash, + runTrashMaintenance, +}; diff --git a/frontend/src/components/TrashContextMenu.vue b/frontend/src/components/TrashContextMenu.vue new file mode 100644 index 00000000..ec6da38e --- /dev/null +++ b/frontend/src/components/TrashContextMenu.vue @@ -0,0 +1,177 @@ + + + diff --git a/frontend/src/components/TrashMenu.vue b/frontend/src/components/TrashMenu.vue new file mode 100644 index 00000000..8be663fd --- /dev/null +++ b/frontend/src/components/TrashMenu.vue @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index a6b2c6a7..32413e1a 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -391,7 +391,8 @@ "filesThumbnails": "Dateien & Miniaturansichten", "security": "Sicherheit", "accessControl": "Zugriffskontrolle", - "adminUsers": "Benutzerverwaltung" + "adminUsers": "Benutzerverwaltung", + "trash": "Papierkorb und Versionen" }, "about": { "subtitle": "Build-Informationen für diese Anwendung anzeigen.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Papierkorb und Versionen", + "subtitle": "Gelöschte Elemente und frühere Dateiversionen werden in einem reservierten Bereich auf ihrem eigenen Volume aufbewahrt und nach ihrer Aufbewahrungsfrist oder bei Platzbedarf entfernt.", + "enabled": "Gelöschte Elemente in den Papierkorb verschieben", + "enabledHelp": "Ausgeschaltet entfernt Löschen Elemente endgültig. Elemente im Papierkorb laufen trotzdem ab.", + "retentionDays": "Aufbewahrung (Tage)", + "retentionHelp": "Von 1 bis 3650 Tage.", + "maxPercent": "Anteil je Volume (%)", + "maxPercentHelp": "Höchstens so viel darf der Papierkorb auf einem Volume belegen, von 1 bis 90 %.", + "maxSize": "Größenlimit je Volume (GiB)", + "maxSizeHelp": "Optional. Es gilt der kleinere Wert von Anteil und Limit.", + "maxSizeNone": "Kein Limit", + "invalid": "Prüfen Sie die Werte: Aufbewahrung 1 bis 3650 Tage, Anteil 1 bis 90 %, eine positive Größe oder keine.", + "saveFailed": "Die Einstellungen konnten nicht gespeichert werden.", + "environmentNote": "Die Standardwerte stammen aus TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT und TRASH_MAX_SIZE.", + "zonesTitle": "Papierkorb je Volume", + "zonesSubtitle": "Jedes Volume bewahrt seinen Papierkorb in einem versteckten Ordner .nextexplorer an seiner Wurzel auf, daher kopiert Löschen nie etwas.", + "zonesEmpty": "Noch wurde nichts in einen Papierkorb gelöscht.", + "zonesFailed": "Der Papierkorb der Volumes konnte nicht geladen werden.", + "verifyFailed": "Die Prüfung konnte nicht ausgeführt werden.", + "maintenanceFailed": "Die Wartung konnte nicht ausgeführt werden.", + "zoneKind": { + "personal": "Persönlicher Ordner von {name}", + "userVolume": "Zugewiesenes Volume {name}" + }, + "available": "Verfügbar", + "unavailable": { + "missing": "Nicht eingehängt oder entfernt", + "replaced": "Ein anderer Datenträger an seiner Stelle", + "unreadable": "Nicht lesbar" + }, + "usage": "{used} von {budget}", + "zoneSummary": "{count} Element, {used} von {budget} | {count} Elemente, {used} von {budget}", + "noBudget": "ohne Limit", + "lastPass": "Letzte Wartung:", + "lastPassPurged": "{count} entfernt | {count} entfernt", + "never": "noch nicht", + "verify": "Prüfen", + "verifying": "Wird geprüft…", + "verifyOk": "Der Papierkorb jedes Volumes ist konsistent.", + "verifyIssues": "{count} Problem gefunden | {count} Probleme gefunden", + "runMaintenance": "Wartung jetzt ausführen", + "running": "Läuft…", + "events": { + "title": "Protokoll", + "evicted": "\"{name}\" vor Ablauf der Aufbewahrung entfernt, um Platz zu schaffen", + "adopted": "\"{name}\" nach einer Unterbrechung wiederhergestellt", + "lost": "Der Inhalt von \"{name}\" fehlte", + "failed": "\"{name}\" konnte nicht entfernt werden", + "breaker": "Zu viele Elemente fehlten auf einmal: nichts wurde geändert", + "forgotten": "\"{name}\" von einem Administrator vergessen", + "versionEvicted": "Eine Version von „{name}“ entfernt, um Platz zu schaffen", + "versionFailed": "Eine Version konnte nicht entfernt werden", + "versionLost": "Einer Version fehlte ihr Inhalt", + "versionTooLarge": "„{name}“ ist größer als der ganze Bereich: Was es ersetzte, wurde nicht behalten", + "versionsBreaker": "Zu viele Versionen fehlen auf einmal: Nichts wurde geändert" + }, + "zoneVersions": "{count} Version, {size} | {count} Versionen, {size}", + "versions": { + "title": "Dateiversionen", + "subtitle": "Jedes Speichern behält, was es ersetzt, ohne es zu kopieren, im selben reservierten Bereich wie der Papierkorb. Ältere Versionen werden mit der Zeit ausgedünnt.", + "enabled": "Frühere Versionen von Dateien behalten", + "enabledHelp": "Unabhängig vom Papierkorb. Ausgeschaltet wird beim Speichern nichts Neues behalten; vorhandene Versionen bleiben bis zu ihrem Ablauf oder ihrer Löschung.", + "keepAllHours": "Alle Versionen behalten (Stunden)", + "hourlyDays": "Dann eine pro Stunde (Tage)", + "dailyDays": "Dann eine pro Tag (Tage)", + "maxPerFile": "Höchstens Versionen pro Datei", + "sessionCheckpointMinutes": "Office-Zwischenstand (Minuten)", + "tiersHelp": "Nach diesen Stufen wird eine Version pro Woche behalten. Eine Office-Bearbeitungssitzung hinterlässt eine Version, dazu höchstens in diesem Abstand einen Zwischenstand. Angeheftete Versionen sind von der Ausdünnung und der Grenze pro Datei ausgenommen.", + "invalid": "Prüfen Sie die Werte: 1 bis 720 Stunden, 1 bis 365 Tage stündlich, 1 bis 3650 Tage täglich, 1 bis 1000 Versionen pro Datei, ein Zwischenstand von 1 bis 1440 Minuten.", + "sharedSpace": "Versionen und Papierkorb teilen sich den reservierten Bereich jedes Volumes. Wird er knapp, gehen zuerst ältere Versionen, dann Papierkorbelemente, dann die letzte Version jeder Datei und zuletzt angeheftete Versionen.", + "environmentNote": "Standardwerte stammen aus VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE und VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "Freigabe entfernen?", "deleteShareMessage": "Personen mit diesem Link können nicht mehr auf das freigegebene Element zugreifen." + }, + "trash": { + "title": "Papierkorb", + "menu": "Papierkorb", + "subtitle": "Elemente werden {count} Tag nach dem Löschen endgültig entfernt. | Elemente werden {count} Tage nach dem Löschen endgültig entfernt.", + "empty": "Der Papierkorb ist leer.", + "disabled": "Der Papierkorb ist ausgeschaltet: Löschen entfernt Elemente endgültig. Was noch hier ist, kann wiederhergestellt werden.", + "unavailable": "Volume nicht verfügbar", + "expiresToday": "Heute", + "expiresIn": "In {count} Tag | In {count} Tagen", + "columns": { + "originalLocation": "Ursprünglicher Ort", + "deletedBy": "Gelöscht von", + "deletedAt": "Gelöscht am", + "expires": "Entfernung", + "modified": "Geändert" + }, + "location": { + "personal": "Persönlicher Ordner", + "unknown": "Unbekannt" + }, + "deletedBy": { + "you": "Sie", + "shareLink": "Freigabelink", + "unknown": "Unbekannt" + }, + "actions": { + "restore": "Wiederherstellen", + "deletePermanently": "Endgültig löschen", + "empty": "Papierkorb leeren", + "openLocation": "Ursprünglichen Ort öffnen", + "selectAll": "Alle auswählen", + "select": "{name} auswählen", + "open": "{name} öffnen", + "restoreFolder": "Ganzen Ordner wiederherstellen", + "restoreTo": "Wiederherstellen nach…" + }, + "confirm": { + "deleteTitle": "Endgültig löschen?", + "deleteMessage": "Dieses Element wird endgültig entfernt. Das kann nicht rückgängig gemacht werden. | Diese {count} Elemente werden endgültig entfernt. Das kann nicht rückgängig gemacht werden.", + "emptyTitle": "Papierkorb leeren?", + "emptyMessage": "Alles in Ihrem Papierkorb wird endgültig entfernt. Das kann nicht rückgängig gemacht werden." + }, + "results": { + "restored": "{count} Element wiederhergestellt | {count} Elemente wiederhergestellt", + "restoredTo": "Wiederhergestellt in {path}", + "renamed": "\"{name}\" wurde als \"{newName}\" wiederhergestellt: Der Name war vergeben.", + "notRestored": "{count} Element konnte nicht wiederhergestellt werden | {count} Elemente konnten nicht wiederhergestellt werden", + "deleted": "{count} Element endgültig gelöscht | {count} Elemente endgültig gelöscht", + "notDeleted": "{count} Element konnte nicht gelöscht werden | {count} Elemente konnten nicht gelöscht werden", + "emptied": "Papierkorb geleert: {count} Element entfernt | Papierkorb geleert: {count} Elemente entfernt", + "leftBehind": "{count} Element konnte nicht entfernt werden und ist noch da. | {count} Elemente konnten nicht entfernt werden und sind noch da." + }, + "reasons": { + "forbidden": "Sie können an seinem Ort nicht mehr schreiben.", + "blocked": "an der Stelle seines Ordners steht jetzt etwas anderes.", + "unavailable": "sein Volume ist nicht verfügbar.", + "busy": "es wird bereits bearbeitet.", + "lost": "sein Inhalt war verschwunden.", + "notFound": "es ist nicht mehr im Papierkorb.", + "unknown": "es konnte nicht bearbeitet werden.", + "destinationForbidden": "Sie dürfen in diesem Ordner nichts anlegen.", + "invalidDestination": "dieser Ordner kann es nicht aufnehmen." + }, + "errors": { + "load": "Der Papierkorb konnte nicht geladen werden.", + "action": "Der Papierkorb konnte das nicht ausführen.", + "loadFolder": "Dieser Ordner konnte nicht geöffnet werden." + }, + "browse": { + "breadcrumb": "Position im Papierkorb", + "deletedFrom": "Gelöscht aus {location} am {date}", + "hint": "Wiederherstellen legt jedes ausgewählte Element an seinen Platz in diesem Ordner zurück. Der Rest bleibt im Papierkorb.", + "empty": "Dieser Ordner ist leer.", + "back": "Zurück zum Papierkorb" + }, + "contextMenu": { + "label": "Aktionen für {name}", + "labelMany": "Aktionen für {count} Elemente", + "open": "Öffnen", + "preview": "Vorschau" + }, + "shares": { + "badge": "Freigegeben", + "badgeTitle": "{count} Freigabelink, im Papierkorb deaktiviert | {count} Freigabelinks, im Papierkorb deaktiviert", + "chooseTitle": "Was geschieht mit den Freigabelinks?", + "chooseMessage": "Zu dem, was Sie wiederherstellen, gehörte {count} Freigabelink. Er funktioniert seit dem Löschen nicht mehr. | Zu dem, was Sie wiederherstellen, gehörten {count} Freigabelinks. Sie funktionieren seit dem Löschen nicht mehr.", + "chooseElsewhere": "Zurückgeholt zeigen sie auf den neuen Ort.", + "keep": "Freigabelinks wiederherstellen", + "drop": "Freigabelinks löschen", + "deleteNotice": "Ihre Freigabelinks werden ebenfalls endgültig gelöscht.", + "restored": "{count} Freigabelink wiederhergestellt | {count} Freigabelinks wiederhergestellt", + "dropped": "{count} Freigabelink gelöscht | {count} Freigabelinks gelöscht" + } } } diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 4d6178af..8c6a7291 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -391,7 +391,8 @@ "filesThumbnails": "Files & Thumbnails", "security": "Security", "accessControl": "Access Control", - "adminUsers": "User Management" + "adminUsers": "User Management", + "trash": "Trash and versions" }, "about": { "subtitle": "View build information for this application.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Trash and versions", + "subtitle": "Deleted items and earlier versions of files are kept in a reserved space on their own volume, and removed after their retention or when that space is needed.", + "enabled": "Send deleted items to the trash", + "enabledHelp": "When off, deleting removes items for good. Items already in the trash still expire.", + "retentionDays": "Retention (days)", + "retentionHelp": "From 1 to 3650 days.", + "maxPercent": "Share of each volume (%)", + "maxPercentHelp": "The most the trash may hold on a volume, from 1 to 90%.", + "maxSize": "Size cap per volume (GiB)", + "maxSizeHelp": "Optional. The smaller of the share and the cap applies.", + "maxSizeNone": "No cap", + "invalid": "Check the values: retention from 1 to 3650 days, share from 1 to 90%, a positive size or none.", + "saveFailed": "The settings could not be saved.", + "environmentNote": "Defaults come from TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT and TRASH_MAX_SIZE.", + "zonesTitle": "Trash by volume", + "zonesSubtitle": "Each volume keeps its trash in a hidden .nextexplorer folder at its root, so deleting never copies anything.", + "zonesEmpty": "Nothing has been deleted into a trash yet.", + "zonesFailed": "The volumes' trash could not be loaded.", + "verifyFailed": "The check could not run.", + "maintenanceFailed": "The maintenance could not run.", + "zoneKind": { + "personal": "Personal folder of {name}", + "userVolume": "Assigned volume {name}" + }, + "available": "Available", + "unavailable": { + "missing": "Not mounted or removed", + "replaced": "Another disk in its place", + "unreadable": "Unreadable" + }, + "usage": "{used} of {budget}", + "zoneSummary": "{count} item, {used} of {budget} | {count} items, {used} of {budget}", + "noBudget": "no limit", + "lastPass": "Last maintenance:", + "lastPassPurged": "{count} removed | {count} removed", + "never": "not yet", + "verify": "Verify", + "verifying": "Verifying…", + "verifyOk": "Every volume's trash is consistent.", + "verifyIssues": "{count} problem found | {count} problems found", + "runMaintenance": "Run maintenance now", + "running": "Running…", + "events": { + "title": "Journal", + "evicted": "\"{name}\" removed before its retention to free space", + "adopted": "\"{name}\" recovered after an interruption", + "lost": "\"{name}\" was missing its content", + "failed": "\"{name}\" could not be removed", + "breaker": "Too many items missing at once: nothing was changed", + "forgotten": "\"{name}\" forgotten by an administrator", + "versionEvicted": "A version of \"{name}\" removed to free space", + "versionFailed": "A version could not be removed", + "versionLost": "A version was missing its content", + "versionTooLarge": "\"{name}\" is larger than the whole space: what it replaced was not kept", + "versionsBreaker": "Too many versions missing at once: nothing was changed" + }, + "zoneVersions": "{count} version, {size} | {count} versions, {size}", + "versions": { + "title": "File versions", + "subtitle": "Each save keeps what it replaces, without copying it, in the same reserved space as the trash. Older versions are thinned out over time.", + "enabled": "Keep earlier versions of files", + "enabledHelp": "Independent of the trash. When off, saves keep nothing new; existing versions stay until they expire or are deleted.", + "keepAllHours": "Keep every version (hours)", + "hourlyDays": "Then one per hour (days)", + "dailyDays": "Then one per day (days)", + "maxPerFile": "Versions per file at most", + "sessionCheckpointMinutes": "Office checkpoint (minutes)", + "tiersHelp": "After these tiers, one version per week is kept. An office editing session leaves one version, plus a checkpoint at most this often. Pinned versions escape the thinning and the per-file limit.", + "invalid": "Check the values: 1 to 720 hours, 1 to 365 days per hour, 1 to 3650 days per day, 1 to 1000 versions per file, a checkpoint from 1 to 1440 minutes.", + "sharedSpace": "Versions and the trash share each volume's reserved space. When it runs short, older versions go first, then trash items, then each file's latest version, and pinned versions last.", + "environmentNote": "Defaults come from VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE and VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ "sharedWithAnyone": "Everyone", "sharedWithUsers": "{count} user | {count} users", "expiresNever": "Never" + }, + "trash": { + "title": "Trash", + "menu": "Trash", + "subtitle": "Items are removed for good {count} day after they were deleted. | Items are removed for good {count} days after they were deleted.", + "empty": "The trash is empty.", + "disabled": "The trash is switched off: deleting removes items for good. What is still here can be restored.", + "unavailable": "Volume unavailable", + "expiresToday": "Today", + "expiresIn": "In {count} day | In {count} days", + "columns": { + "originalLocation": "Original location", + "deletedBy": "Deleted by", + "deletedAt": "Deleted on", + "expires": "Removed", + "modified": "Modified" + }, + "location": { + "personal": "Personal folder", + "unknown": "Unknown" + }, + "deletedBy": { + "you": "You", + "shareLink": "Share link", + "unknown": "Unknown" + }, + "actions": { + "restore": "Restore", + "deletePermanently": "Delete permanently", + "empty": "Empty trash", + "openLocation": "Open original location", + "selectAll": "Select all", + "select": "Select {name}", + "open": "Open {name}", + "restoreFolder": "Restore whole folder", + "restoreTo": "Restore to…" + }, + "confirm": { + "deleteTitle": "Delete permanently?", + "deleteMessage": "This item will be removed for good. This cannot be undone. | These {count} items will be removed for good. This cannot be undone.", + "emptyTitle": "Empty the trash?", + "emptyMessage": "Everything in your trash will be removed for good. This cannot be undone." + }, + "results": { + "restored": "{count} item restored | {count} items restored", + "restoredTo": "Restored to {path}", + "renamed": "\"{name}\" was restored as \"{newName}\": its name was taken.", + "notRestored": "{count} item could not be restored | {count} items could not be restored", + "deleted": "{count} item deleted permanently | {count} items deleted permanently", + "notDeleted": "{count} item could not be deleted | {count} items could not be deleted", + "emptied": "Trash emptied: {count} item removed | Trash emptied: {count} items removed", + "leftBehind": "{count} item could not be removed and is still there. | {count} items could not be removed and are still there." + }, + "reasons": { + "forbidden": "you can no longer write where it was.", + "blocked": "something now stands where its folder was.", + "unavailable": "its volume is not available.", + "busy": "it is already being handled.", + "lost": "its content had disappeared.", + "notFound": "it is no longer in the trash.", + "unknown": "it could not be handled.", + "destinationForbidden": "you cannot create items in that folder.", + "invalidDestination": "that folder cannot receive it." + }, + "errors": { + "load": "The trash could not be loaded.", + "action": "The trash could not do that.", + "loadFolder": "This folder could not be opened." + }, + "browse": { + "breadcrumb": "Location in the trash", + "deletedFrom": "Deleted from {location} on {date}", + "hint": "Restore puts each selected item back in its place inside this folder. The rest stays in the trash.", + "empty": "This folder is empty.", + "back": "Back to the trash" + }, + "contextMenu": { + "label": "Actions for {name}", + "labelMany": "Actions for {count} items", + "open": "Open", + "preview": "Preview" + }, + "shares": { + "badge": "Shared", + "badgeTitle": "{count} share link, switched off while in the trash | {count} share links, switched off while in the trash", + "chooseTitle": "What about the share links?", + "chooseMessage": "{count} share link belonged to what you are restoring. It has stopped working since the deletion. | {count} share links belonged to what you are restoring. They have stopped working since the deletion.", + "chooseElsewhere": "Brought back, they will point to the new location.", + "keep": "Restore the share links", + "drop": "Delete the share links", + "deleteNotice": "Their share links will be deleted for good too.", + "restored": "{count} share link brought back | {count} share links brought back", + "dropped": "{count} share link deleted | {count} share links deleted" + } } } diff --git a/frontend/src/i18n/locales/es.json b/frontend/src/i18n/locales/es.json index 15ea9ea9..768b8a04 100644 --- a/frontend/src/i18n/locales/es.json +++ b/frontend/src/i18n/locales/es.json @@ -391,7 +391,8 @@ "filesThumbnails": "Archivos y miniaturas", "security": "Seguridad", "accessControl": "Control de acceso", - "adminUsers": "Gestión de usuarios" + "adminUsers": "Gestión de usuarios", + "trash": "Papelera y versiones" }, "about": { "subtitle": "Ver la información de compilación de esta aplicación.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Papelera y versiones", + "subtitle": "Los elementos eliminados y las versiones anteriores de los archivos se guardan en un espacio reservado de su propio volumen y se eliminan al terminar su retención o cuando se necesita ese espacio.", + "enabled": "Enviar los elementos eliminados a la papelera", + "enabledHelp": "Desactivada, eliminar borra definitivamente. Los elementos que ya están en la papelera siguen caducando.", + "retentionDays": "Retención (días)", + "retentionHelp": "De 1 a 3650 días.", + "maxPercent": "Parte de cada volumen (%)", + "maxPercentHelp": "Lo máximo que la papelera puede ocupar en un volumen, de 1 a 90 %.", + "maxSize": "Tamaño máximo por volumen (GiB)", + "maxSizeHelp": "Opcional. Se aplica el menor entre la parte y el límite.", + "maxSizeNone": "Sin límite", + "invalid": "Revisa los valores: retención de 1 a 3650 días, parte de 1 a 90 %, un tamaño positivo o ninguno.", + "saveFailed": "No se pudieron guardar los ajustes.", + "environmentNote": "Los valores por defecto vienen de TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT y TRASH_MAX_SIZE.", + "zonesTitle": "Papelera por volumen", + "zonesSubtitle": "Cada volumen guarda su papelera en una carpeta oculta .nextexplorer en su raíz, así que eliminar nunca copia nada.", + "zonesEmpty": "Todavía no se ha eliminado nada a una papelera.", + "zonesFailed": "No se pudo cargar la papelera de los volúmenes.", + "verifyFailed": "No se pudo ejecutar la verificación.", + "maintenanceFailed": "No se pudo ejecutar el mantenimiento.", + "zoneKind": { + "personal": "Carpeta personal de {name}", + "userVolume": "Volumen asignado {name}" + }, + "available": "Disponible", + "unavailable": { + "missing": "No montado o eliminado", + "replaced": "Otro disco en su lugar", + "unreadable": "Ilegible" + }, + "usage": "{used} de {budget}", + "zoneSummary": "{count} elemento, {used} de {budget} | {count} elementos, {used} de {budget}", + "noBudget": "sin límite", + "lastPass": "Último mantenimiento:", + "lastPassPurged": "{count} eliminado | {count} eliminados", + "never": "todavía no", + "verify": "Verificar", + "verifying": "Verificando…", + "verifyOk": "La papelera de cada volumen es coherente.", + "verifyIssues": "{count} problema encontrado | {count} problemas encontrados", + "runMaintenance": "Ejecutar mantenimiento ahora", + "running": "En curso…", + "events": { + "title": "Registro", + "evicted": "\"{name}\" eliminado antes de su retención para liberar espacio", + "adopted": "\"{name}\" recuperado tras una interrupción", + "lost": "Faltaba el contenido de \"{name}\"", + "failed": "\"{name}\" no se pudo eliminar", + "breaker": "Faltaban demasiados elementos a la vez: no se cambió nada", + "forgotten": "\"{name}\" olvidado por un administrador", + "versionEvicted": "Una versión de «{name}» eliminada para liberar espacio", + "versionFailed": "No se pudo eliminar una versión", + "versionLost": "Faltaba el contenido de una versión", + "versionTooLarge": "«{name}» supera todo el espacio: lo que reemplazaba no se conservó", + "versionsBreaker": "Demasiadas versiones faltan a la vez: no se cambió nada" + }, + "zoneVersions": "{count} versión, {size} | {count} versiones, {size}", + "versions": { + "title": "Versiones de archivos", + "subtitle": "Cada guardado conserva lo que reemplaza, sin copiarlo, en el mismo espacio reservado que la papelera. Las versiones antiguas se espacian con el tiempo.", + "enabled": "Conservar las versiones anteriores de los archivos", + "enabledHelp": "Independiente de la papelera. Desactivado, los guardados no conservan nada nuevo; las versiones existentes permanecen hasta que caducan o se eliminan.", + "keepAllHours": "Conservar todas las versiones (horas)", + "hourlyDays": "Después una por hora (días)", + "dailyDays": "Después una por día (días)", + "maxPerFile": "Máximo de versiones por archivo", + "sessionCheckpointMinutes": "Punto de control de ofimática (minutos)", + "tiersHelp": "Tras estos niveles se conserva una versión por semana. Una sesión de edición de ofimática deja una versión, más un punto de control como mucho con esta frecuencia. Las versiones fijadas escapan al espaciado y al límite por archivo.", + "invalid": "Revisa los valores: de 1 a 720 horas, de 1 a 365 días por hora, de 1 a 3650 días por día, de 1 a 1000 versiones por archivo, un punto de control de 1 a 1440 minutos.", + "sharedSpace": "Las versiones y la papelera comparten el espacio reservado de cada volumen. Cuando falta, primero se van las versiones antiguas, luego los elementos de la papelera, luego la última versión de cada archivo y, por último, las versiones fijadas.", + "environmentNote": "Los valores predeterminados vienen de VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE y VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "¿Eliminar enlace compartido?", "deleteShareMessage": "Las personas con este enlace ya no podrán acceder al elemento compartido." + }, + "trash": { + "title": "Papelera", + "menu": "Papelera", + "subtitle": "Los elementos se eliminan definitivamente {count} día después de borrarlos. | Los elementos se eliminan definitivamente {count} días después de borrarlos.", + "empty": "La papelera está vacía.", + "disabled": "La papelera está desactivada: eliminar borra definitivamente. Lo que aún está aquí se puede restaurar.", + "unavailable": "Volumen no disponible", + "expiresToday": "Hoy", + "expiresIn": "En {count} día | En {count} días", + "columns": { + "originalLocation": "Ubicación original", + "deletedBy": "Eliminado por", + "deletedAt": "Eliminado el", + "expires": "Eliminación", + "modified": "Modificado" + }, + "location": { + "personal": "Carpeta personal", + "unknown": "Desconocida" + }, + "deletedBy": { + "you": "Tú", + "shareLink": "Enlace compartido", + "unknown": "Desconocido" + }, + "actions": { + "restore": "Restaurar", + "deletePermanently": "Eliminar definitivamente", + "empty": "Vaciar papelera", + "openLocation": "Abrir ubicación original", + "selectAll": "Seleccionar todo", + "select": "Seleccionar {name}", + "open": "Abrir {name}", + "restoreFolder": "Restaurar la carpeta entera", + "restoreTo": "Restaurar en…" + }, + "confirm": { + "deleteTitle": "¿Eliminar definitivamente?", + "deleteMessage": "Este elemento se eliminará definitivamente. Esta acción no se puede deshacer. | Estos {count} elementos se eliminarán definitivamente. Esta acción no se puede deshacer.", + "emptyTitle": "¿Vaciar la papelera?", + "emptyMessage": "Todo lo que hay en tu papelera se eliminará definitivamente. Esta acción no se puede deshacer." + }, + "results": { + "restored": "{count} elemento restaurado | {count} elementos restaurados", + "restoredTo": "Restaurado en {path}", + "renamed": "\"{name}\" se restauró como \"{newName}\": su nombre estaba ocupado.", + "notRestored": "{count} elemento no se pudo restaurar | {count} elementos no se pudieron restaurar", + "deleted": "{count} elemento eliminado definitivamente | {count} elementos eliminados definitivamente", + "notDeleted": "{count} elemento no se pudo eliminar | {count} elementos no se pudieron eliminar", + "emptied": "Papelera vaciada: {count} elemento eliminado | Papelera vaciada: {count} elementos eliminados", + "leftBehind": "{count} elemento no se pudo eliminar y sigue ahí. | {count} elementos no se pudieron eliminar y siguen ahí." + }, + "reasons": { + "forbidden": "ya no puedes escribir donde estaba.", + "blocked": "ahora hay algo en el lugar de su carpeta.", + "unavailable": "su volumen no está disponible.", + "busy": "ya se está procesando.", + "lost": "su contenido había desaparecido.", + "notFound": "ya no está en la papelera.", + "unknown": "no se pudo procesar.", + "destinationForbidden": "no puede crear elementos en esa carpeta.", + "invalidDestination": "esa carpeta no puede recibirlo." + }, + "errors": { + "load": "No se pudo cargar la papelera.", + "action": "La papelera no pudo hacer eso.", + "loadFolder": "No se pudo abrir esta carpeta." + }, + "browse": { + "breadcrumb": "Ubicación en la papelera", + "deletedFrom": "Eliminado de {location} el {date}", + "hint": "Restaurar devuelve cada elemento seleccionado a su lugar dentro de esta carpeta. El resto permanece en la papelera.", + "empty": "Esta carpeta está vacía.", + "back": "Volver a la papelera" + }, + "contextMenu": { + "label": "Acciones para {name}", + "labelMany": "Acciones para {count} elementos", + "open": "Abrir", + "preview": "Vista previa" + }, + "shares": { + "badge": "Compartido", + "badgeTitle": "{count} enlace compartido, desactivado mientras está en la papelera | {count} enlaces compartidos, desactivados mientras están en la papelera", + "chooseTitle": "¿Qué hacer con los enlaces compartidos?", + "chooseMessage": "{count} enlace compartido pertenecía a lo que restaura. No funciona desde la eliminación. | {count} enlaces compartidos pertenecían a lo que restaura. No funcionan desde la eliminación.", + "chooseElsewhere": "Si se recuperan, apuntarán a la nueva ubicación.", + "keep": "Restaurar los enlaces compartidos", + "drop": "Eliminar los enlaces compartidos", + "deleteNotice": "Sus enlaces compartidos también se eliminarán definitivamente.", + "restored": "{count} enlace compartido recuperado | {count} enlaces compartidos recuperados", + "dropped": "{count} enlace compartido eliminado | {count} enlaces compartidos eliminados" + } } } diff --git a/frontend/src/i18n/locales/fr.json b/frontend/src/i18n/locales/fr.json index e058f366..6411cc89 100644 --- a/frontend/src/i18n/locales/fr.json +++ b/frontend/src/i18n/locales/fr.json @@ -391,7 +391,8 @@ "filesThumbnails": "Fichiers et vignettes", "security": "Sécurité", "accessControl": "Contrôle d'accès", - "adminUsers": "Gestion des utilisateurs" + "adminUsers": "Gestion des utilisateurs", + "trash": "Corbeille et versions" }, "about": { "subtitle": "Afficher les informations de build de cette application.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Corbeille et versions", + "subtitle": "Les éléments supprimés et les versions antérieures des fichiers sont conservés dans un espace réservé sur leur propre volume, puis supprimés à la fin de leur rétention ou lorsque cet espace est nécessaire.", + "enabled": "Envoyer les éléments supprimés à la corbeille", + "enabledHelp": "Désactivée, la suppression efface définitivement. Les éléments déjà dans la corbeille expirent toujours.", + "retentionDays": "Rétention (jours)", + "retentionHelp": "De 1 à 3650 jours.", + "maxPercent": "Part de chaque volume (%)", + "maxPercentHelp": "Le maximum que la corbeille peut occuper sur un volume, de 1 à 90 %.", + "maxSize": "Taille maximale par volume (Gio)", + "maxSizeHelp": "Facultatif. La plus petite des deux limites s'applique.", + "maxSizeNone": "Aucune limite", + "invalid": "Vérifiez les valeurs : rétention de 1 à 3650 jours, part de 1 à 90 %, une taille positive ou aucune.", + "saveFailed": "Les réglages n'ont pas pu être enregistrés.", + "environmentNote": "Les valeurs par défaut viennent de TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT et TRASH_MAX_SIZE.", + "zonesTitle": "Corbeille par volume", + "zonesSubtitle": "Chaque volume garde sa corbeille dans un dossier caché .nextexplorer à sa racine : supprimer ne copie jamais rien.", + "zonesEmpty": "Rien n'a encore été supprimé vers une corbeille.", + "zonesFailed": "La corbeille des volumes n'a pas pu être chargée.", + "verifyFailed": "La vérification n'a pas pu s'exécuter.", + "maintenanceFailed": "L'entretien n'a pas pu s'exécuter.", + "zoneKind": { + "personal": "Dossier personnel de {name}", + "userVolume": "Volume attribué {name}" + }, + "available": "Disponible", + "unavailable": { + "missing": "Non monté ou supprimé", + "replaced": "Un autre disque à sa place", + "unreadable": "Illisible" + }, + "usage": "{used} sur {budget}", + "zoneSummary": "{count} élément, {used} sur {budget} | {count} éléments, {used} sur {budget}", + "noBudget": "sans limite", + "lastPass": "Dernier entretien :", + "lastPassPurged": "{count} supprimé | {count} supprimés", + "never": "pas encore", + "verify": "Vérifier", + "verifying": "Vérification…", + "verifyOk": "La corbeille de chaque volume est cohérente.", + "verifyIssues": "{count} problème trouvé | {count} problèmes trouvés", + "runMaintenance": "Lancer l'entretien maintenant", + "running": "En cours…", + "events": { + "title": "Journal", + "evicted": "\"{name}\" supprimé avant la fin de sa rétention pour libérer de l'espace", + "adopted": "\"{name}\" récupéré après une interruption", + "lost": "Le contenu de \"{name}\" avait disparu", + "failed": "\"{name}\" n'a pas pu être supprimé", + "breaker": "Trop d'éléments manquants d'un coup : rien n'a été modifié", + "forgotten": "\"{name}\" oublié par un administrateur", + "versionEvicted": "Une version de « {name} » supprimée pour libérer de l'espace", + "versionFailed": "Une version n'a pas pu être supprimée", + "versionLost": "Le contenu d'une version était introuvable", + "versionTooLarge": "« {name} » dépasse tout l'espace : ce qu'il remplaçait n'a pas été gardé", + "versionsBreaker": "Trop de versions introuvables d'un coup : rien n'a été modifié" + }, + "zoneVersions": "{count} version, {size} | {count} versions, {size}", + "versions": { + "title": "Versions de fichiers", + "subtitle": "Chaque enregistrement garde ce qu'il remplace, sans le copier, dans le même espace réservé que la corbeille. Les versions anciennes sont espacées avec le temps.", + "enabled": "Garder les versions antérieures des fichiers", + "enabledHelp": "Indépendant de la corbeille. Désactivé, les enregistrements ne gardent plus rien ; les versions existantes restent jusqu'à leur expiration ou leur suppression.", + "keepAllHours": "Garder toutes les versions (heures)", + "hourlyDays": "Puis une par heure (jours)", + "dailyDays": "Puis une par jour (jours)", + "maxPerFile": "Versions par fichier au plus", + "sessionCheckpointMinutes": "Point d'étape bureautique (minutes)", + "tiersHelp": "Au-delà de ces paliers, une version par semaine est gardée. Une session d'édition bureautique laisse une version, plus un point d'étape au plus à cette fréquence. Les versions épinglées échappent à l'espacement et à la limite par fichier.", + "invalid": "Vérifiez les valeurs : 1 à 720 heures, 1 à 365 jours par heure, 1 à 3650 jours par jour, 1 à 1000 versions par fichier, un point d'étape de 1 à 1440 minutes.", + "sharedSpace": "Les versions et la corbeille partagent l'espace réservé de chaque volume. Quand il manque, les versions anciennes partent d'abord, puis les éléments de la corbeille, puis la dernière version de chaque fichier, et les versions épinglées en dernier.", + "environmentNote": "Les valeurs par défaut viennent de VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE et VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ "sharedWithAnyone": "Tout le monde", "sharedWithUsers": "{count} utilisateur | {count} utilisateurs", "expiresNever": "Jamais" + }, + "trash": { + "title": "Corbeille", + "menu": "Corbeille", + "subtitle": "Les éléments sont supprimés définitivement {count} jour après leur suppression. | Les éléments sont supprimés définitivement {count} jours après leur suppression.", + "empty": "La corbeille est vide.", + "disabled": "La corbeille est désactivée : supprimer efface définitivement. Ce qui s'y trouve encore peut être restauré.", + "unavailable": "Volume indisponible", + "expiresToday": "Aujourd'hui", + "expiresIn": "Dans {count} jour | Dans {count} jours", + "columns": { + "originalLocation": "Emplacement d'origine", + "deletedBy": "Supprimé par", + "deletedAt": "Supprimé le", + "expires": "Suppression", + "modified": "Modifié le" + }, + "location": { + "personal": "Dossier personnel", + "unknown": "Inconnu" + }, + "deletedBy": { + "you": "Vous", + "shareLink": "Lien de partage", + "unknown": "Inconnu" + }, + "actions": { + "restore": "Restaurer", + "deletePermanently": "Supprimer définitivement", + "empty": "Vider la corbeille", + "openLocation": "Ouvrir l'emplacement d'origine", + "selectAll": "Tout sélectionner", + "select": "Sélectionner {name}", + "open": "Ouvrir {name}", + "restoreFolder": "Restaurer tout le dossier", + "restoreTo": "Restaurer vers…" + }, + "confirm": { + "deleteTitle": "Supprimer définitivement ?", + "deleteMessage": "Cet élément sera supprimé définitivement. Cette action est irréversible. | Ces {count} éléments seront supprimés définitivement. Cette action est irréversible.", + "emptyTitle": "Vider la corbeille ?", + "emptyMessage": "Tout ce que contient votre corbeille sera supprimé définitivement. Cette action est irréversible." + }, + "results": { + "restored": "{count} élément restauré | {count} éléments restaurés", + "restoredTo": "Restauré dans {path}", + "renamed": "\"{name}\" a été restauré sous le nom \"{newName}\" : son nom était pris.", + "notRestored": "{count} élément n'a pas pu être restauré | {count} éléments n'ont pas pu être restaurés", + "deleted": "{count} élément supprimé définitivement | {count} éléments supprimés définitivement", + "notDeleted": "{count} élément n'a pas pu être supprimé | {count} éléments n'ont pas pu être supprimés", + "emptied": "Corbeille vidée : {count} élément supprimé | Corbeille vidée : {count} éléments supprimés", + "leftBehind": "{count} élément n'a pas pu être supprimé et y est toujours. | {count} éléments n'ont pas pu être supprimés et y sont toujours." + }, + "reasons": { + "forbidden": "vous ne pouvez plus écrire à son emplacement d'origine.", + "blocked": "quelque chose occupe désormais la place de son dossier.", + "unavailable": "son volume n'est pas disponible.", + "busy": "il est déjà en cours de traitement.", + "lost": "son contenu avait disparu.", + "notFound": "il n'est plus dans la corbeille.", + "unknown": "il n'a pas pu être traité.", + "destinationForbidden": "vous ne pouvez pas créer d'éléments dans ce dossier.", + "invalidDestination": "ce dossier ne peut pas le recevoir." + }, + "errors": { + "load": "La corbeille n'a pas pu être chargée.", + "action": "La corbeille n'a pas pu effectuer cette action.", + "loadFolder": "Ce dossier n'a pas pu être ouvert." + }, + "browse": { + "breadcrumb": "Emplacement dans la corbeille", + "deletedFrom": "Supprimé de {location} le {date}", + "hint": "Restaurer remet chaque élément sélectionné à sa place dans ce dossier. Le reste demeure dans la corbeille.", + "empty": "Ce dossier est vide.", + "back": "Retour à la corbeille" + }, + "contextMenu": { + "label": "Actions pour {name}", + "labelMany": "Actions pour {count} éléments", + "open": "Ouvrir", + "preview": "Aperçu" + }, + "shares": { + "badge": "Partagé", + "badgeTitle": "{count} lien de partage, désactivé tant qu'il est dans la corbeille | {count} liens de partage, désactivés tant qu'ils sont dans la corbeille", + "chooseTitle": "Que faire des liens de partage ?", + "chooseMessage": "{count} lien de partage appartenait à ce que vous restaurez. Il ne fonctionne plus depuis la suppression. | {count} liens de partage appartenaient à ce que vous restaurez. Ils ne fonctionnent plus depuis la suppression.", + "chooseElsewhere": "Rétablis, ils pointeront vers le nouvel emplacement.", + "keep": "Rétablir les liens de partage", + "drop": "Supprimer les liens de partage", + "deleteNotice": "Leurs liens de partage seront eux aussi supprimés définitivement.", + "restored": "{count} lien de partage rétabli | {count} liens de partage rétablis", + "dropped": "{count} lien de partage supprimé | {count} liens de partage supprimés" + } } } diff --git a/frontend/src/i18n/locales/hi.json b/frontend/src/i18n/locales/hi.json index a52a09c7..cba1c211 100644 --- a/frontend/src/i18n/locales/hi.json +++ b/frontend/src/i18n/locales/hi.json @@ -391,7 +391,8 @@ "filesThumbnails": "फ़ाइलें और थंबनेल", "security": "सुरक्षा", "accessControl": "एक्सेस कंट्रोल", - "adminUsers": "उपयोगकर्ता प्रबंधन" + "adminUsers": "उपयोगकर्ता प्रबंधन", + "trash": "रीसायकल बिन और संस्करण" }, "about": { "subtitle": "इस एप्लिकेशन के बिल्ड संबंधी जानकारी देखें।", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "रीसायकल बिन और संस्करण", + "subtitle": "हटाए गए आइटम और फ़ाइलों के पिछले संस्करण उनके अपने वॉल्यूम पर एक आरक्षित स्थान में रखे जाते हैं, और अवधि पूरी होने पर या उस स्थान की ज़रूरत पड़ने पर हटा दिए जाते हैं।", + "enabled": "हटाए गए आइटम रीसायकल बिन में भेजें", + "enabledHelp": "बंद होने पर हटाने से आइटम स्थायी रूप से हट जाते हैं। रीसायकल बिन में पहले से मौजूद आइटम फिर भी समाप्त होते हैं।", + "retentionDays": "रखने की अवधि (दिन)", + "retentionHelp": "1 से 3650 दिन।", + "maxPercent": "प्रत्येक वॉल्यूम का हिस्सा (%)", + "maxPercentHelp": "किसी वॉल्यूम पर रीसायकल बिन अधिकतम कितना ले सकता है, 1 से 90 %।", + "maxSize": "प्रति वॉल्यूम आकार सीमा (GiB)", + "maxSizeHelp": "वैकल्पिक। हिस्से और सीमा में से छोटा लागू होता है।", + "maxSizeNone": "कोई सीमा नहीं", + "invalid": "मान जाँचें: अवधि 1 से 3650 दिन, हिस्सा 1 से 90 %, सकारात्मक आकार या कोई नहीं।", + "saveFailed": "सेटिंग्स सहेजी नहीं जा सकीं।", + "environmentNote": "डिफ़ॉल्ट मान TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT और TRASH_MAX_SIZE से आते हैं।", + "zonesTitle": "वॉल्यूम के अनुसार रीसायकल बिन", + "zonesSubtitle": "हर वॉल्यूम अपना रीसायकल बिन अपनी जड़ में छिपे फ़ोल्डर .nextexplorer में रखता है, इसलिए हटाना कभी कुछ कॉपी नहीं करता।", + "zonesEmpty": "अभी तक किसी रीसायकल बिन में कुछ नहीं हटाया गया।", + "zonesFailed": "वॉल्यूम का रीसायकल बिन लोड नहीं हो सका।", + "verifyFailed": "जाँच नहीं चल सकी।", + "maintenanceFailed": "रखरखाव नहीं चल सका।", + "zoneKind": { + "personal": "{name} का निजी फ़ोल्डर", + "userVolume": "सौंपा गया वॉल्यूम {name}" + }, + "available": "उपलब्ध", + "unavailable": { + "missing": "माउंट नहीं या हटाया गया", + "replaced": "उसकी जगह कोई दूसरी डिस्क", + "unreadable": "पढ़ा नहीं जा सकता" + }, + "usage": "{budget} में से {used}", + "zoneSummary": "{count} आइटम, {budget} में से {used} | {count} आइटम, {budget} में से {used}", + "noBudget": "कोई सीमा नहीं", + "lastPass": "पिछला रखरखाव:", + "lastPassPurged": "{count} हटाया गया | {count} हटाए गए", + "never": "अभी नहीं", + "verify": "जाँचें", + "verifying": "जाँच हो रही है…", + "verifyOk": "हर वॉल्यूम का रीसायकल बिन सुसंगत है।", + "verifyIssues": "{count} समस्या मिली | {count} समस्याएँ मिलीं", + "runMaintenance": "अभी रखरखाव चलाएँ", + "running": "चल रहा है…", + "events": { + "title": "लॉग", + "evicted": "जगह खाली करने के लिए \"{name}\" अवधि पूरी होने से पहले हटाया गया", + "adopted": "रुकावट के बाद \"{name}\" वापस पाया गया", + "lost": "\"{name}\" की सामग्री गायब थी", + "failed": "\"{name}\" हटाया नहीं जा सका", + "breaker": "एक साथ बहुत सारे आइटम गायब थे: कुछ नहीं बदला गया", + "forgotten": "\"{name}\" को एक व्यवस्थापक ने भुला दिया", + "versionEvicted": "जगह खाली करने के लिए \"{name}\" का एक संस्करण हटाया गया", + "versionFailed": "एक संस्करण हटाया नहीं जा सका", + "versionLost": "एक संस्करण की सामग्री नहीं मिली", + "versionTooLarge": "\"{name}\" पूरे स्थान से बड़ी है: जो उसने बदला, वह नहीं रखा गया", + "versionsBreaker": "एक साथ बहुत सारे संस्करण गायब हैं: कुछ नहीं बदला गया" + }, + "zoneVersions": "{count} संस्करण, {size} | {count} संस्करण, {size}", + "versions": { + "title": "फ़ाइल संस्करण", + "subtitle": "हर बार सहेजने पर जो बदला जाता है, वह बिना प्रतिलिपि बनाए, रीसायकल बिन वाले ही आरक्षित स्थान में रखा जाता है। पुराने संस्करण समय के साथ कम किए जाते हैं।", + "enabled": "फ़ाइलों के पिछले संस्करण रखें", + "enabledHelp": "रीसायकल बिन से स्वतंत्र। बंद होने पर सहेजने से कुछ नया नहीं रखा जाता; मौजूदा संस्करण अवधि पूरी होने या हटाए जाने तक रहते हैं।", + "keepAllHours": "सभी संस्करण रखें (घंटे)", + "hourlyDays": "फिर हर घंटे एक (दिन)", + "dailyDays": "फिर हर दिन एक (दिन)", + "maxPerFile": "प्रति फ़ाइल अधिकतम संस्करण", + "sessionCheckpointMinutes": "ऑफ़िस चेकपॉइंट (मिनट)", + "tiersHelp": "इन स्तरों के बाद हर सप्ताह एक संस्करण रखा जाता है। ऑफ़िस संपादन का एक सत्र एक संस्करण छोड़ता है, और इस अंतराल से अधिक बार नहीं एक चेकपॉइंट। पिन किए गए संस्करण छँटाई और प्रति फ़ाइल सीमा से मुक्त रहते हैं।", + "invalid": "मान जाँचें: 1 से 720 घंटे, प्रति घंटा 1 से 365 दिन, प्रति दिन 1 से 3650 दिन, प्रति फ़ाइल 1 से 1000 संस्करण, 1 से 1440 मिनट का चेकपॉइंट।", + "sharedSpace": "संस्करण और रीसायकल बिन हर वॉल्यूम का आरक्षित स्थान साझा करते हैं। जगह कम पड़ने पर पहले पुराने संस्करण हटते हैं, फिर रीसायकल बिन के आइटम, फिर हर फ़ाइल का नवीनतम संस्करण, और अंत में पिन किए गए संस्करण।", + "environmentNote": "डिफ़ॉल्ट मान VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE और VERSIONS_SESSION_CHECKPOINT_MINUTES से आते हैं।" + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "शेयर हटाएँ?", "deleteShareMessage": "इस लिंक वाले लोग अब साझा किए गए आइटम तक पहुँच नहीं कर पाएँगे।" + }, + "trash": { + "title": "रीसायकल बिन", + "menu": "रीसायकल बिन", + "subtitle": "आइटम हटाए जाने के {count} दिन बाद स्थायी रूप से हटा दिए जाते हैं। | आइटम हटाए जाने के {count} दिन बाद स्थायी रूप से हटा दिए जाते हैं।", + "empty": "रीसायकल बिन खाली है।", + "disabled": "रीसायकल बिन बंद है: हटाने से आइटम स्थायी रूप से हट जाते हैं। जो अभी यहाँ है उसे पुनर्स्थापित किया जा सकता है।", + "unavailable": "वॉल्यूम उपलब्ध नहीं", + "expiresToday": "आज", + "expiresIn": "{count} दिन में | {count} दिनों में", + "columns": { + "originalLocation": "मूल स्थान", + "deletedBy": "किसने हटाया", + "deletedAt": "हटाने की तारीख", + "expires": "स्थायी रूप से हटना", + "modified": "संशोधित" + }, + "location": { + "personal": "निजी फ़ोल्डर", + "unknown": "अज्ञात" + }, + "deletedBy": { + "you": "आप", + "shareLink": "शेयर लिंक", + "unknown": "अज्ञात" + }, + "actions": { + "restore": "पुनर्स्थापित करें", + "deletePermanently": "स्थायी रूप से हटाएँ", + "empty": "रीसायकल बिन खाली करें", + "openLocation": "मूल स्थान खोलें", + "selectAll": "सभी चुनें", + "select": "{name} चुनें", + "open": "{name} खोलें", + "restoreFolder": "पूरा फ़ोल्डर पुनर्स्थापित करें", + "restoreTo": "कहीं और पुनर्स्थापित करें…" + }, + "confirm": { + "deleteTitle": "स्थायी रूप से हटाएँ?", + "deleteMessage": "यह आइटम स्थायी रूप से हटा दिया जाएगा। यह क्रिया वापस नहीं की जा सकती। | ये {count} आइटम स्थायी रूप से हटा दिए जाएँगे। यह क्रिया वापस नहीं की जा सकती।", + "emptyTitle": "रीसायकल बिन खाली करें?", + "emptyMessage": "आपके रीसायकल बिन में सब कुछ स्थायी रूप से हटा दिया जाएगा। यह क्रिया वापस नहीं की जा सकती।" + }, + "results": { + "restored": "{count} आइटम पुनर्स्थापित हुआ | {count} आइटम पुनर्स्थापित हुए", + "restoredTo": "{path} में पुनर्स्थापित", + "renamed": "\"{name}\" को \"{newName}\" नाम से पुनर्स्थापित किया गया: नाम पहले से इस्तेमाल में था।", + "notRestored": "{count} आइटम पुनर्स्थापित नहीं हो सका | {count} आइटम पुनर्स्थापित नहीं हो सके", + "deleted": "{count} आइटम स्थायी रूप से हटाया गया | {count} आइटम स्थायी रूप से हटाए गए", + "notDeleted": "{count} आइटम हटाया नहीं जा सका | {count} आइटम हटाए नहीं जा सके", + "emptied": "रीसायकल बिन खाली किया गया: {count} आइटम हटाया गया | रीसायकल बिन खाली किया गया: {count} आइटम हटाए गए", + "leftBehind": "{count} आइटम हटाया नहीं जा सका और अभी भी वहीं है। | {count} आइटम हटाए नहीं जा सके और अभी भी वहीं हैं।" + }, + "reasons": { + "forbidden": "आप अब उसके मूल स्थान पर लिख नहीं सकते।", + "blocked": "उसके फ़ोल्डर की जगह अब कुछ और है।", + "unavailable": "उसका वॉल्यूम उपलब्ध नहीं है।", + "busy": "उस पर पहले से काम चल रहा है।", + "lost": "उसकी सामग्री गायब हो गई थी।", + "notFound": "वह अब रीसायकल बिन में नहीं है।", + "unknown": "उसे संसाधित नहीं किया जा सका।", + "destinationForbidden": "आप उस फ़ोल्डर में आइटम नहीं बना सकते।", + "invalidDestination": "वह फ़ोल्डर इसे नहीं ले सकता।" + }, + "errors": { + "load": "रीसायकल बिन लोड नहीं हो सका।", + "action": "रीसायकल बिन यह नहीं कर सका।", + "loadFolder": "यह फ़ोल्डर खोला नहीं जा सका।" + }, + "browse": { + "breadcrumb": "रीसायकल बिन में स्थान", + "deletedFrom": "{location} से {date} को हटाया गया", + "hint": "पुनर्स्थापित करें हर चुने गए आइटम को इस फ़ोल्डर में उसकी जगह वापस रखता है। बाकी रीसायकल बिन में ही रहता है।", + "empty": "यह फ़ोल्डर खाली है।", + "back": "रीसायकल बिन पर वापस जाएँ" + }, + "contextMenu": { + "label": "{name} के लिए कार्रवाइयाँ", + "labelMany": "{count} आइटम के लिए कार्रवाइयाँ", + "open": "खोलें", + "preview": "पूर्वावलोकन" + }, + "shares": { + "badge": "साझा", + "badgeTitle": "{count} साझा लिंक, रीसायकल बिन में रहने तक बंद | {count} साझा लिंक, रीसायकल बिन में रहने तक बंद", + "chooseTitle": "साझा लिंक का क्या करें?", + "chooseMessage": "आप जो पुनर्स्थापित कर रहे हैं उसका {count} साझा लिंक था। हटाए जाने के बाद से यह काम नहीं करता। | आप जो पुनर्स्थापित कर रहे हैं उसके {count} साझा लिंक थे। हटाए जाने के बाद से ये काम नहीं करते।", + "chooseElsewhere": "वापस लाए जाने पर ये नए स्थान की ओर इशारा करेंगे।", + "keep": "साझा लिंक पुनर्स्थापित करें", + "drop": "साझा लिंक हटाएँ", + "deleteNotice": "इनके साझा लिंक भी स्थायी रूप से हटा दिए जाएँगे।", + "restored": "{count} साझा लिंक वापस लाया गया | {count} साझा लिंक वापस लाए गए", + "dropped": "{count} साझा लिंक हटाया गया | {count} साझा लिंक हटाए गए" + } } } diff --git a/frontend/src/i18n/locales/it.json b/frontend/src/i18n/locales/it.json index 2198aa34..3a3edcea 100644 --- a/frontend/src/i18n/locales/it.json +++ b/frontend/src/i18n/locales/it.json @@ -391,7 +391,8 @@ "filesThumbnails": "File e miniature", "security": "Sicurezza", "accessControl": "Controllo accessi", - "adminUsers": "Gestione utenti" + "adminUsers": "Gestione utenti", + "trash": "Cestino e versioni" }, "about": { "subtitle": "Visualizza le informazioni di build per questa applicazione.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Cestino e versioni", + "subtitle": "Gli elementi eliminati e le versioni precedenti dei file sono conservati in uno spazio riservato sul loro volume e rimossi alla fine della conservazione o quando serve spazio.", + "enabled": "Sposta gli elementi eliminati nel cestino", + "enabledHelp": "Se disattivato, eliminare cancella definitivamente. Gli elementi già nel cestino scadono comunque.", + "retentionDays": "Conservazione (giorni)", + "retentionHelp": "Da 1 a 3650 giorni.", + "maxPercent": "Quota di ogni volume (%)", + "maxPercentHelp": "Il massimo che il cestino può occupare su un volume, da 1 a 90 %.", + "maxSize": "Limite di dimensione per volume (GiB)", + "maxSizeHelp": "Facoltativo. Si applica il minore tra quota e limite.", + "maxSizeNone": "Nessun limite", + "invalid": "Controlla i valori: conservazione da 1 a 3650 giorni, quota da 1 a 90 %, una dimensione positiva o nessuna.", + "saveFailed": "Impossibile salvare le impostazioni.", + "environmentNote": "I valori predefiniti provengono da TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT e TRASH_MAX_SIZE.", + "zonesTitle": "Cestino per volume", + "zonesSubtitle": "Ogni volume tiene il suo cestino in una cartella nascosta .nextexplorer nella sua radice, quindi eliminare non copia mai nulla.", + "zonesEmpty": "Non è ancora stato eliminato nulla in un cestino.", + "zonesFailed": "Impossibile caricare il cestino dei volumi.", + "verifyFailed": "Impossibile eseguire la verifica.", + "maintenanceFailed": "Impossibile eseguire la manutenzione.", + "zoneKind": { + "personal": "Cartella personale di {name}", + "userVolume": "Volume assegnato {name}" + }, + "available": "Disponibile", + "unavailable": { + "missing": "Non montato o rimosso", + "replaced": "Un altro disco al suo posto", + "unreadable": "Illeggibile" + }, + "usage": "{used} su {budget}", + "zoneSummary": "{count} elemento, {used} su {budget} | {count} elementi, {used} su {budget}", + "noBudget": "nessun limite", + "lastPass": "Ultima manutenzione:", + "lastPassPurged": "{count} eliminato | {count} eliminati", + "never": "non ancora", + "verify": "Verifica", + "verifying": "Verifica in corso…", + "verifyOk": "Il cestino di ogni volume è coerente.", + "verifyIssues": "{count} problema trovato | {count} problemi trovati", + "runMaintenance": "Esegui la manutenzione ora", + "running": "In corso…", + "events": { + "title": "Registro", + "evicted": "\"{name}\" eliminato prima della fine della conservazione per liberare spazio", + "adopted": "\"{name}\" recuperato dopo un'interruzione", + "lost": "Il contenuto di \"{name}\" mancava", + "failed": "\"{name}\" non è stato eliminato", + "breaker": "Troppi elementi mancanti in una volta: nulla è stato modificato", + "forgotten": "\"{name}\" dimenticato da un amministratore", + "versionEvicted": "Una versione di «{name}» rimossa per liberare spazio", + "versionFailed": "Impossibile rimuovere una versione", + "versionLost": "Mancava il contenuto di una versione", + "versionTooLarge": "«{name}» supera l'intero spazio: ciò che sostituiva non è stato conservato", + "versionsBreaker": "Troppe versioni mancanti insieme: non è stato modificato nulla" + }, + "zoneVersions": "{count} versione, {size} | {count} versioni, {size}", + "versions": { + "title": "Versioni dei file", + "subtitle": "Ogni salvataggio conserva ciò che sostituisce, senza copiarlo, nello stesso spazio riservato del cestino. Le versioni più vecchie vengono diradate nel tempo.", + "enabled": "Conserva le versioni precedenti dei file", + "enabledHelp": "Indipendente dal cestino. Se disattivato, i salvataggi non conservano nulla di nuovo; le versioni esistenti restano fino alla scadenza o all'eliminazione.", + "keepAllHours": "Conserva tutte le versioni (ore)", + "hourlyDays": "Poi una all'ora (giorni)", + "dailyDays": "Poi una al giorno (giorni)", + "maxPerFile": "Versioni per file al massimo", + "sessionCheckpointMinutes": "Punto di controllo office (minuti)", + "tiersHelp": "Dopo queste fasce si conserva una versione a settimana. Una sessione di modifica office lascia una versione, più un punto di controllo al massimo con questa frequenza. Le versioni fissate sfuggono al diradamento e al limite per file.", + "invalid": "Controlla i valori: da 1 a 720 ore, da 1 a 365 giorni orari, da 1 a 3650 giorni giornalieri, da 1 a 1000 versioni per file, un punto di controllo da 1 a 1440 minuti.", + "sharedSpace": "Le versioni e il cestino condividono lo spazio riservato di ogni volume. Quando scarseggia, se ne vanno prima le versioni vecchie, poi gli elementi del cestino, poi l'ultima versione di ogni file e infine le versioni fissate.", + "environmentNote": "I valori predefiniti provengono da VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE e VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "Rimuovere la condivisione?", "deleteShareMessage": "Le persone con questo link non potranno più accedere all’elemento condiviso." + }, + "trash": { + "title": "Cestino", + "menu": "Cestino", + "subtitle": "Gli elementi vengono eliminati definitivamente {count} giorno dopo la cancellazione. | Gli elementi vengono eliminati definitivamente {count} giorni dopo la cancellazione.", + "empty": "Il cestino è vuoto.", + "disabled": "Il cestino è disattivato: eliminare cancella definitivamente. Ciò che è ancora qui può essere ripristinato.", + "unavailable": "Volume non disponibile", + "expiresToday": "Oggi", + "expiresIn": "Tra {count} giorno | Tra {count} giorni", + "columns": { + "originalLocation": "Posizione originale", + "deletedBy": "Eliminato da", + "deletedAt": "Eliminato il", + "expires": "Rimozione", + "modified": "Modificato" + }, + "location": { + "personal": "Cartella personale", + "unknown": "Sconosciuta" + }, + "deletedBy": { + "you": "Tu", + "shareLink": "Link di condivisione", + "unknown": "Sconosciuto" + }, + "actions": { + "restore": "Ripristina", + "deletePermanently": "Elimina definitivamente", + "empty": "Svuota cestino", + "openLocation": "Apri posizione originale", + "selectAll": "Seleziona tutto", + "select": "Seleziona {name}", + "open": "Apri {name}", + "restoreFolder": "Ripristina l'intera cartella", + "restoreTo": "Ripristina in…" + }, + "confirm": { + "deleteTitle": "Eliminare definitivamente?", + "deleteMessage": "Questo elemento sarà eliminato definitivamente. L'azione non può essere annullata. | Questi {count} elementi saranno eliminati definitivamente. L'azione non può essere annullata.", + "emptyTitle": "Svuotare il cestino?", + "emptyMessage": "Tutto ciò che è nel tuo cestino sarà eliminato definitivamente. L'azione non può essere annullata." + }, + "results": { + "restored": "{count} elemento ripristinato | {count} elementi ripristinati", + "restoredTo": "Ripristinato in {path}", + "renamed": "\"{name}\" è stato ripristinato come \"{newName}\": il nome era già in uso.", + "notRestored": "{count} elemento non è stato ripristinato | {count} elementi non sono stati ripristinati", + "deleted": "{count} elemento eliminato definitivamente | {count} elementi eliminati definitivamente", + "notDeleted": "{count} elemento non è stato eliminato | {count} elementi non sono stati eliminati", + "emptied": "Cestino svuotato: {count} elemento eliminato | Cestino svuotato: {count} elementi eliminati", + "leftBehind": "{count} elemento non è stato eliminato ed è ancora lì. | {count} elementi non sono stati eliminati e sono ancora lì." + }, + "reasons": { + "forbidden": "non puoi più scrivere dove si trovava.", + "blocked": "ora c'è qualcos'altro al posto della sua cartella.", + "unavailable": "il suo volume non è disponibile.", + "busy": "è già in lavorazione.", + "lost": "il suo contenuto era scomparso.", + "notFound": "non è più nel cestino.", + "unknown": "non è stato possibile gestirlo.", + "destinationForbidden": "non puoi creare elementi in quella cartella.", + "invalidDestination": "quella cartella non può riceverlo." + }, + "errors": { + "load": "Impossibile caricare il cestino.", + "action": "Il cestino non ha potuto eseguire l'azione.", + "loadFolder": "Impossibile aprire questa cartella." + }, + "browse": { + "breadcrumb": "Posizione nel cestino", + "deletedFrom": "Eliminato da {location} il {date}", + "hint": "Ripristina rimette ogni elemento selezionato al suo posto in questa cartella. Il resto rimane nel cestino.", + "empty": "Questa cartella è vuota.", + "back": "Torna al cestino" + }, + "contextMenu": { + "label": "Azioni per {name}", + "labelMany": "Azioni per {count} elementi", + "open": "Apri", + "preview": "Anteprima" + }, + "shares": { + "badge": "Condiviso", + "badgeTitle": "{count} link di condivisione, disattivato finché è nel cestino | {count} link di condivisione, disattivati finché sono nel cestino", + "chooseTitle": "Che cosa fare dei link di condivisione?", + "chooseMessage": "{count} link di condivisione apparteneva a ciò che stai ripristinando. Non funziona dall'eliminazione. | {count} link di condivisione appartenevano a ciò che stai ripristinando. Non funzionano dall'eliminazione.", + "chooseElsewhere": "Se ripristinati, punteranno alla nuova posizione.", + "keep": "Ripristina i link di condivisione", + "drop": "Elimina i link di condivisione", + "deleteNotice": "Anche i loro link di condivisione verranno eliminati definitivamente.", + "restored": "{count} link di condivisione ripristinato | {count} link di condivisione ripristinati", + "dropped": "{count} link di condivisione eliminato | {count} link di condivisione eliminati" + } } } diff --git a/frontend/src/i18n/locales/ko.json b/frontend/src/i18n/locales/ko.json index 033d218f..a3434800 100644 --- a/frontend/src/i18n/locales/ko.json +++ b/frontend/src/i18n/locales/ko.json @@ -391,7 +391,8 @@ "filesThumbnails": "파일/썸네일", "security": "보안", "accessControl": "접근 제어", - "adminUsers": "유저 관리" + "adminUsers": "유저 관리", + "trash": "휴지통 및 버전" }, "about": { "subtitle": "애플리케이션의 빌드 정보를 확인합니다.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC 제공자", "oidcLinkedProfile": "연결된 프로필", "noOidcProfileMessage": "이 계정과 연결된 OIDC 프로필이 없습니다." + }, + "trash": { + "title": "휴지통 및 버전", + "subtitle": "삭제된 항목과 파일의 이전 버전은 해당 볼륨의 예약 공간에 보관되며, 보존 기간이 끝나거나 공간이 필요할 때 제거됩니다.", + "enabled": "삭제한 항목을 휴지통으로 보내기", + "enabledHelp": "끄면 삭제 시 영구적으로 제거됩니다. 이미 휴지통에 있는 항목은 계속 만료됩니다.", + "retentionDays": "보관 기간(일)", + "retentionHelp": "1일부터 3650일까지.", + "maxPercent": "각 볼륨에서의 비율(%)", + "maxPercentHelp": "휴지통이 한 볼륨에서 차지할 수 있는 최대치, 1~90%.", + "maxSize": "볼륨당 크기 제한(GiB)", + "maxSizeHelp": "선택 사항. 비율과 제한 중 작은 값이 적용됩니다.", + "maxSizeNone": "제한 없음", + "invalid": "값을 확인하세요: 보관 기간 1~3650일, 비율 1~90%, 양수 크기 또는 비워 두기.", + "saveFailed": "설정을 저장할 수 없습니다.", + "environmentNote": "기본값은 TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT, TRASH_MAX_SIZE에서 가져옵니다.", + "zonesTitle": "볼륨별 휴지통", + "zonesSubtitle": "각 볼륨은 루트의 숨김 폴더 .nextexplorer에 휴지통을 두므로 삭제할 때 아무것도 복사하지 않습니다.", + "zonesEmpty": "아직 휴지통으로 삭제된 항목이 없습니다.", + "zonesFailed": "볼륨의 휴지통을 불러올 수 없습니다.", + "verifyFailed": "검사를 실행할 수 없습니다.", + "maintenanceFailed": "유지 관리를 실행할 수 없습니다.", + "zoneKind": { + "personal": "{name}의 개인 폴더", + "userVolume": "할당된 볼륨 {name}" + }, + "available": "사용 가능", + "unavailable": { + "missing": "마운트되지 않았거나 제거됨", + "replaced": "다른 디스크로 교체됨", + "unreadable": "읽을 수 없음" + }, + "usage": "{budget} 중 {used}", + "zoneSummary": "항목 {count}개, {budget} 중 {used}", + "noBudget": "제한 없음", + "lastPass": "마지막 유지 관리:", + "lastPassPurged": "{count}개 삭제됨", + "never": "아직 없음", + "verify": "검사", + "verifying": "검사 중…", + "verifyOk": "모든 볼륨의 휴지통이 일관됩니다.", + "verifyIssues": "문제 {count}개 발견", + "runMaintenance": "지금 유지 관리 실행", + "running": "실행 중…", + "events": { + "title": "기록", + "evicted": "공간 확보를 위해 보관 기간 전에 \"{name}\" 삭제됨", + "adopted": "중단 후 \"{name}\" 복구됨", + "lost": "\"{name}\"의 내용이 없었음", + "failed": "\"{name}\"을(를) 삭제할 수 없음", + "breaker": "한꺼번에 너무 많은 항목이 사라짐: 아무것도 변경하지 않음", + "forgotten": "관리자가 \"{name}\"을(를) 잊음", + "versionEvicted": "공간 확보를 위해 \"{name}\"의 버전을 제거했습니다", + "versionFailed": "버전을 제거하지 못했습니다", + "versionLost": "버전의 내용이 없었습니다", + "versionTooLarge": "\"{name}\"이(가) 전체 공간보다 커서 대체된 내용을 보관하지 않았습니다", + "versionsBreaker": "한꺼번에 너무 많은 버전이 사라져 아무것도 변경하지 않았습니다" + }, + "zoneVersions": "버전 {count}개, {size}", + "versions": { + "title": "파일 버전", + "subtitle": "저장할 때마다 대체되는 내용을 복사 없이 휴지통과 같은 예약 공간에 보관합니다. 오래된 버전은 시간이 지나며 간격을 두고 정리됩니다.", + "enabled": "파일의 이전 버전 보관", + "enabledHelp": "휴지통과 별개로 동작합니다. 끄면 저장해도 새로 보관되지 않으며, 기존 버전은 만료되거나 삭제될 때까지 남습니다.", + "keepAllHours": "모든 버전 보관 (시간)", + "hourlyDays": "이후 시간당 하나 (일)", + "dailyDays": "이후 하루에 하나 (일)", + "maxPerFile": "파일당 최대 버전 수", + "sessionCheckpointMinutes": "오피스 체크포인트 (분)", + "tiersHelp": "이 단계 이후에는 주당 하나의 버전이 보관됩니다. 오피스 편집 세션은 하나의 버전을 남기며, 이 간격보다 자주 체크포인트를 만들지 않습니다. 고정된 버전은 간격 정리와 파일당 한도에서 제외됩니다.", + "invalid": "값을 확인하세요: 1~720시간, 시간 단위 1~365일, 일 단위 1~3650일, 파일당 1~1000개 버전, 체크포인트 1~1440분.", + "sharedSpace": "버전과 휴지통은 각 볼륨의 예약 공간을 함께 사용합니다. 공간이 부족하면 오래된 버전, 휴지통 항목, 각 파일의 최신 버전 순으로 제거되며 고정된 버전은 마지막에 제거됩니다.", + "environmentNote": "기본값은 VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE, VERSIONS_SESSION_CHECKPOINT_MINUTES에서 가져옵니다." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "공유를 제거할까요?", "deleteShareMessage": "이 링크가 있는 사용자는 더 이상 공유 항목에 접근할 수 없습니다." + }, + "trash": { + "title": "휴지통", + "menu": "휴지통", + "subtitle": "항목은 삭제 후 {count}일이 지나면 영구 삭제됩니다.", + "empty": "휴지통이 비어 있습니다.", + "disabled": "휴지통이 꺼져 있습니다. 삭제하면 영구적으로 제거됩니다. 여기에 남은 항목은 복원할 수 있습니다.", + "unavailable": "볼륨을 사용할 수 없음", + "expiresToday": "오늘", + "expiresIn": "{count}일 후", + "columns": { + "originalLocation": "원래 위치", + "deletedBy": "삭제한 사람", + "deletedAt": "삭제한 날짜", + "expires": "영구 삭제", + "modified": "수정한 날짜" + }, + "location": { + "personal": "개인 폴더", + "unknown": "알 수 없음" + }, + "deletedBy": { + "you": "나", + "shareLink": "공유 링크", + "unknown": "알 수 없음" + }, + "actions": { + "restore": "복원", + "deletePermanently": "영구 삭제", + "empty": "휴지통 비우기", + "openLocation": "원래 위치 열기", + "selectAll": "모두 선택", + "select": "{name} 선택", + "open": "{name} 열기", + "restoreFolder": "폴더 전체 복원", + "restoreTo": "다른 위치에 복원…" + }, + "confirm": { + "deleteTitle": "영구 삭제하시겠습니까?", + "deleteMessage": "항목 {count}개가 영구 삭제됩니다. 이 작업은 되돌릴 수 없습니다.", + "emptyTitle": "휴지통을 비우시겠습니까?", + "emptyMessage": "휴지통의 모든 항목이 영구 삭제됩니다. 이 작업은 되돌릴 수 없습니다." + }, + "results": { + "restored": "항목 {count}개 복원됨", + "restoredTo": "{path}에 복원됨", + "renamed": "\"{name}\"은(는) 이름이 이미 사용 중이어서 \"{newName}\"(으)로 복원되었습니다.", + "notRestored": "항목 {count}개를 복원할 수 없음", + "deleted": "항목 {count}개 영구 삭제됨", + "notDeleted": "항목 {count}개를 삭제할 수 없음", + "emptied": "휴지통을 비웠습니다: 항목 {count}개 삭제됨", + "leftBehind": "항목 {count}개를 삭제할 수 없어 아직 남아 있습니다." + }, + "reasons": { + "forbidden": "원래 위치에 더 이상 쓸 수 없습니다.", + "blocked": "원래 폴더 자리에 이제 다른 것이 있습니다.", + "unavailable": "볼륨을 사용할 수 없습니다.", + "busy": "이미 처리 중입니다.", + "lost": "내용이 사라졌습니다.", + "notFound": "더 이상 휴지통에 없습니다.", + "unknown": "처리할 수 없습니다.", + "destinationForbidden": "그 폴더에 항목을 만들 수 없습니다.", + "invalidDestination": "그 폴더로는 옮길 수 없습니다." + }, + "errors": { + "load": "휴지통을 불러올 수 없습니다.", + "action": "휴지통에서 해당 작업을 수행할 수 없습니다.", + "loadFolder": "이 폴더를 열 수 없습니다." + }, + "browse": { + "breadcrumb": "휴지통 내 위치", + "deletedFrom": "{date}에 {location}에서 삭제됨", + "hint": "복원하면 선택한 항목이 이 폴더 안의 원래 자리로 돌아갑니다. 나머지는 휴지통에 남습니다.", + "empty": "이 폴더는 비어 있습니다.", + "back": "휴지통으로 돌아가기" + }, + "contextMenu": { + "label": "{name} 작업", + "labelMany": "항목 {count}개 작업", + "open": "열기", + "preview": "미리 보기" + }, + "shares": { + "badge": "공유됨", + "badgeTitle": "공유 링크 {count}개, 휴지통에 있는 동안 꺼짐", + "chooseTitle": "공유 링크는 어떻게 할까요?", + "chooseMessage": "복원하는 항목에 공유 링크 {count}개가 있었습니다. 삭제된 뒤로 작동하지 않습니다.", + "chooseElsewhere": "되살리면 새 위치를 가리킵니다.", + "keep": "공유 링크 복원", + "drop": "공유 링크 삭제", + "deleteNotice": "공유 링크도 함께 영구적으로 삭제됩니다.", + "restored": "공유 링크 {count}개를 되살렸습니다", + "dropped": "공유 링크 {count}개를 삭제했습니다" + } } } diff --git a/frontend/src/i18n/locales/nl.json b/frontend/src/i18n/locales/nl.json index 92267103..20fa3eb2 100644 --- a/frontend/src/i18n/locales/nl.json +++ b/frontend/src/i18n/locales/nl.json @@ -391,7 +391,8 @@ "filesThumbnails": "Bestanden & miniaturen", "security": "Beveiliging", "accessControl": "Toegangscontrole", - "adminUsers": "Gebruikersbeheer" + "adminUsers": "Gebruikersbeheer", + "trash": "Prullenbak en versies" }, "about": { "subtitle": "Versieinformatie voor deze applicatie.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Gekoppeld profiel", "noOidcProfileMessage": "Geen OIDC-profielen gekoppeld aan dit account." + }, + "trash": { + "title": "Prullenbak en versies", + "subtitle": "Verwijderde items en eerdere versies van bestanden worden bewaard in een gereserveerde ruimte op hun eigen volume, en gewist na hun bewaartermijn of wanneer die ruimte nodig is.", + "enabled": "Verwijderde items naar de prullenbak sturen", + "enabledHelp": "Indien uitgeschakeld, wist verwijderen items definitief. Items die al in de prullenbak liggen, verlopen nog steeds.", + "retentionDays": "Bewaartermijn (dagen)", + "retentionHelp": "Van 1 tot 3650 dagen.", + "maxPercent": "Aandeel van elk volume (%)", + "maxPercentHelp": "Het maximum dat de prullenbak op een volume mag innemen, van 1 tot 90%.", + "maxSize": "Maximale grootte per volume (GiB)", + "maxSizeHelp": "Optioneel. De kleinste van het aandeel en het maximum geldt.", + "maxSizeNone": "Geen maximum", + "invalid": "Controleer de waarden: bewaartermijn van 1 tot 3650 dagen, aandeel van 1 tot 90%, een positieve grootte of geen.", + "saveFailed": "De instellingen konden niet worden opgeslagen.", + "environmentNote": "Standaardwaarden komen uit TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT en TRASH_MAX_SIZE.", + "zonesTitle": "Prullenbak per volume", + "zonesSubtitle": "Elk volume bewaart zijn prullenbak in een verborgen .nextexplorer-map in de hoofdmap, zodat verwijderen nooit iets kopieert.", + "zonesEmpty": "Er is nog niets naar een prullenbak verwijderd.", + "zonesFailed": "De prullenbakken van de volumes konden niet worden geladen.", + "verifyFailed": "De controle kon niet worden uitgevoerd.", + "maintenanceFailed": "Het onderhoud kon niet worden uitgevoerd.", + "zoneKind": { + "personal": "Persoonlijke map van {name}", + "userVolume": "Toegewezen volume {name}" + }, + "available": "Beschikbaar", + "unavailable": { + "missing": "Niet gekoppeld of verwijderd", + "replaced": "Een andere schijf op die plek", + "unreadable": "Onleesbaar" + }, + "usage": "{used} van {budget}", + "zoneSummary": "{count} item, {used} van {budget} | {count} items, {used} van {budget}", + "noBudget": "geen limiet", + "lastPass": "Laatste onderhoud:", + "lastPassPurged": "{count} gewist | {count} gewist", + "never": "nog niet", + "verify": "Controleren", + "verifying": "Bezig met controleren…", + "verifyOk": "De prullenbak van elk volume is consistent.", + "verifyIssues": "{count} probleem gevonden | {count} problemen gevonden", + "runMaintenance": "Onderhoud nu uitvoeren", + "running": "Bezig…", + "events": { + "title": "Logboek", + "evicted": "\"{name}\" vóór het einde van de bewaartermijn gewist om ruimte vrij te maken", + "adopted": "\"{name}\" hersteld na een onderbreking", + "lost": "Van \"{name}\" ontbrak de inhoud", + "failed": "\"{name}\" kon niet worden gewist", + "breaker": "Te veel items tegelijk verdwenen: er is niets gewijzigd", + "forgotten": "\"{name}\" vergeten door een beheerder", + "versionEvicted": "Een versie van \"{name}\" gewist om ruimte vrij te maken", + "versionFailed": "Een versie kon niet worden gewist", + "versionLost": "Van een versie ontbrak de inhoud", + "versionTooLarge": "\"{name}\" is groter dan de volledige ruimte: wat het verving, is niet bewaard", + "versionsBreaker": "Te veel versies tegelijk verdwenen: er is niets gewijzigd" + }, + "zoneVersions": "{count} versie, {size} | {count} versies, {size}", + "versions": { + "title": "Bestandsversies", + "subtitle": "Elke opslag bewaart wat hij vervangt, zonder het te kopiëren, in dezelfde gereserveerde ruimte als de prullenbak. Oudere versies worden in de loop van de tijd uitgedund.", + "enabled": "Eerdere versies van bestanden bewaren", + "enabledHelp": "Los van de prullenbak. Indien uitgeschakeld, bewaart opslaan niets nieuws; bestaande versies blijven tot ze verlopen of worden verwijderd.", + "keepAllHours": "Elke versie bewaren (uren)", + "hourlyDays": "Daarna één per uur (dagen)", + "dailyDays": "Daarna één per dag (dagen)", + "maxPerFile": "Maximaal aantal versies per bestand", + "sessionCheckpointMinutes": "Office-tussenstand (minuten)", + "tiersHelp": "Na deze stappen wordt één versie per week bewaard. Een Office-bewerksessie laat één versie achter, plus hooguit zo vaak een tussenstand. Vastgezette versies ontsnappen aan het uitdunnen en aan de limiet per bestand.", + "invalid": "Controleer de waarden: 1 tot 720 uur, 1 tot 365 dagen per uur, 1 tot 3650 dagen per dag, 1 tot 1000 versies per bestand, een tussenstand van 1 tot 1440 minuten.", + "sharedSpace": "Versies en de prullenbak delen de gereserveerde ruimte van elk volume. Wordt die krap, dan gaan eerst oudere versies, daarna items uit de prullenbak, dan de laatste versie van elk bestand en als laatste vastgezette versies.", + "environmentNote": "Standaardwaarden komen uit VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE en VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "share": { @@ -632,5 +707,99 @@ "rotateRight": "Naar rechts draaien", "zoomIn": "Inzoomen", "zoomOut": "Uitzoomen" + }, + "trash": { + "title": "Prullenbak", + "menu": "Prullenbak", + "subtitle": "Items worden {count} dag na het verwijderen definitief gewist. | Items worden {count} dagen na het verwijderen definitief gewist.", + "empty": "De prullenbak is leeg.", + "disabled": "De prullenbak is uitgeschakeld: verwijderen wist items definitief. Wat hier nog staat, kan worden teruggezet.", + "unavailable": "Volume niet beschikbaar", + "expiresToday": "Vandaag", + "expiresIn": "Over {count} dag | Over {count} dagen", + "columns": { + "originalLocation": "Oorspronkelijke locatie", + "deletedBy": "Verwijderd door", + "deletedAt": "Verwijderd op", + "expires": "Gewist", + "modified": "Gewijzigd" + }, + "location": { + "personal": "Persoonlijke map", + "unknown": "Onbekend" + }, + "deletedBy": { + "you": "U", + "shareLink": "Deellink", + "unknown": "Onbekend" + }, + "actions": { + "restore": "Terugzetten", + "deletePermanently": "Definitief verwijderen", + "empty": "Prullenbak leegmaken", + "openLocation": "Oorspronkelijke locatie openen", + "selectAll": "Alles selecteren", + "select": "{name} selecteren", + "open": "{name} openen", + "restoreFolder": "Hele map terugzetten", + "restoreTo": "Terugzetten naar…" + }, + "confirm": { + "deleteTitle": "Definitief verwijderen?", + "deleteMessage": "Dit item wordt definitief gewist. Dit kan niet ongedaan worden gemaakt. | Deze {count} items worden definitief gewist. Dit kan niet ongedaan worden gemaakt.", + "emptyTitle": "Prullenbak leegmaken?", + "emptyMessage": "Alles in uw prullenbak wordt definitief gewist. Dit kan niet ongedaan worden gemaakt." + }, + "results": { + "restored": "{count} item teruggezet | {count} items teruggezet", + "restoredTo": "Teruggezet naar {path}", + "renamed": "\"{name}\" is teruggezet als \"{newName}\": de naam was al in gebruik.", + "notRestored": "{count} item kon niet worden teruggezet | {count} items konden niet worden teruggezet", + "deleted": "{count} item definitief verwijderd | {count} items definitief verwijderd", + "notDeleted": "{count} item kon niet worden verwijderd | {count} items konden niet worden verwijderd", + "emptied": "Prullenbak leeggemaakt: {count} item gewist | Prullenbak leeggemaakt: {count} items gewist", + "leftBehind": "{count} item kon niet worden gewist en staat er nog. | {count} items konden niet worden gewist en staan er nog." + }, + "reasons": { + "forbidden": "u kunt niet meer schrijven op de plek waar het stond.", + "blocked": "er staat nu iets anders op de plek van de map.", + "unavailable": "het volume is niet beschikbaar.", + "busy": "het wordt al verwerkt.", + "lost": "de inhoud was verdwenen.", + "notFound": "het staat niet meer in de prullenbak.", + "unknown": "het kon niet worden verwerkt.", + "destinationForbidden": "u kunt in die map geen items aanmaken.", + "invalidDestination": "die map kan het niet ontvangen." + }, + "errors": { + "load": "De prullenbak kon niet worden geladen.", + "action": "De prullenbak kon dit niet uitvoeren.", + "loadFolder": "Deze map kon niet worden geopend." + }, + "browse": { + "breadcrumb": "Locatie in de prullenbak", + "deletedFrom": "Verwijderd uit {location} op {date}", + "hint": "Terugzetten zet elk geselecteerd item terug op zijn plek in deze map. De rest blijft in de prullenbak.", + "empty": "Deze map is leeg.", + "back": "Terug naar de prullenbak" + }, + "contextMenu": { + "label": "Acties voor {name}", + "labelMany": "Acties voor {count} items", + "open": "Openen", + "preview": "Voorbeeld" + }, + "shares": { + "badge": "Gedeeld", + "badgeTitle": "{count} deellink, uitgeschakeld zolang het in de prullenbak ligt | {count} deellinks, uitgeschakeld zolang het in de prullenbak ligt", + "chooseTitle": "Wat gebeurt er met de deellinks?", + "chooseMessage": "{count} deellink hoorde bij wat u terugzet. Die werkt niet meer sinds het verwijderen. | {count} deellinks hoorden bij wat u terugzet. Die werken niet meer sinds het verwijderen.", + "chooseElsewhere": "Als ze terugkomen, wijzen ze naar de nieuwe locatie.", + "keep": "Deellinks herstellen", + "drop": "Deellinks verwijderen", + "deleteNotice": "Hun deellinks worden ook definitief verwijderd.", + "restored": "{count} deellink hersteld | {count} deellinks hersteld", + "dropped": "{count} deellink verwijderd | {count} deellinks verwijderd" + } } } diff --git a/frontend/src/i18n/locales/pl.json b/frontend/src/i18n/locales/pl.json index f398edda..f9f7a1b3 100644 --- a/frontend/src/i18n/locales/pl.json +++ b/frontend/src/i18n/locales/pl.json @@ -391,7 +391,8 @@ "filesThumbnails": "Pliki i miniatury", "security": "Bezpieczeństwo", "accessControl": "Kontrola dostępu", - "adminUsers": "Zarządzanie użytkownikami" + "adminUsers": "Zarządzanie użytkownikami", + "trash": "Kosz i wersje" }, "about": { "subtitle": "Wyświetl informacje o kompilacji tej aplikacji.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Kosz i wersje", + "subtitle": "Usunięte elementy i wcześniejsze wersje plików są przechowywane w zarezerwowanej przestrzeni na ich własnym woluminie i usuwane po okresie przechowywania lub gdy ta przestrzeń jest potrzebna.", + "enabled": "Przenoś usunięte elementy do kosza", + "enabledHelp": "Po wyłączeniu usuwanie jest trwałe. Elementy już w koszu nadal wygasają.", + "retentionDays": "Przechowywanie (dni)", + "retentionHelp": "Od 1 do 3650 dni.", + "maxPercent": "Udział każdego woluminu (%)", + "maxPercentHelp": "Najwięcej, ile kosz może zająć na woluminie, od 1 do 90 %.", + "maxSize": "Limit rozmiaru na wolumin (GiB)", + "maxSizeHelp": "Opcjonalnie. Obowiązuje mniejsza z wartości: udział lub limit.", + "maxSizeNone": "Bez limitu", + "invalid": "Sprawdź wartości: przechowywanie od 1 do 3650 dni, udział od 1 do 90 %, dodatni rozmiar lub brak.", + "saveFailed": "Nie udało się zapisać ustawień.", + "environmentNote": "Wartości domyślne pochodzą z TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT i TRASH_MAX_SIZE.", + "zonesTitle": "Kosz według woluminu", + "zonesSubtitle": "Każdy wolumin trzyma swój kosz w ukrytym folderze .nextexplorer w katalogu głównym, więc usuwanie nigdy niczego nie kopiuje.", + "zonesEmpty": "Nic jeszcze nie trafiło do kosza.", + "zonesFailed": "Nie udało się wczytać kosza woluminów.", + "verifyFailed": "Nie udało się uruchomić sprawdzania.", + "maintenanceFailed": "Nie udało się uruchomić konserwacji.", + "zoneKind": { + "personal": "Folder osobisty użytkownika {name}", + "userVolume": "Przydzielony wolumin {name}" + }, + "available": "Dostępny", + "unavailable": { + "missing": "Niezamontowany lub usunięty", + "replaced": "Inny dysk w jego miejscu", + "unreadable": "Nieczytelny" + }, + "usage": "{used} z {budget}", + "zoneSummary": "{count} element, {used} z {budget} | {count} elementy, {used} z {budget} | {count} elementów, {used} z {budget}", + "noBudget": "bez limitu", + "lastPass": "Ostatnia konserwacja:", + "lastPassPurged": "usunięto {count} | usunięto {count} | usunięto {count}", + "never": "jeszcze nie", + "verify": "Sprawdź", + "verifying": "Sprawdzanie…", + "verifyOk": "Kosz każdego woluminu jest spójny.", + "verifyIssues": "Znaleziono {count} problem | Znaleziono {count} problemy | Znaleziono {count} problemów", + "runMaintenance": "Uruchom konserwację teraz", + "running": "W toku…", + "events": { + "title": "Dziennik", + "evicted": "\"{name}\" usunięto przed końcem przechowywania, aby zwolnić miejsce", + "adopted": "\"{name}\" odzyskano po przerwaniu", + "lost": "Brakowało zawartości \"{name}\"", + "failed": "Nie udało się usunąć \"{name}\"", + "breaker": "Zbyt wiele elementów zniknęło naraz: niczego nie zmieniono", + "forgotten": "\"{name}\" zapomniany przez administratora", + "versionEvicted": "Usunięto wersję „{name}”, aby zwolnić miejsce", + "versionFailed": "Nie udało się usunąć wersji", + "versionLost": "Brakowało zawartości wersji", + "versionTooLarge": "„{name}” jest większy niż cała przestrzeń: to, co zastąpił, nie zostało zachowane", + "versionsBreaker": "Brakuje naraz zbyt wielu wersji: nic nie zmieniono" + }, + "zoneVersions": "{count} wersja, {size} | {count} wersje, {size} | {count} wersji, {size}", + "versions": { + "title": "Wersje plików", + "subtitle": "Każdy zapis zachowuje to, co zastępuje, bez kopiowania, w tej samej zarezerwowanej przestrzeni co kosz. Starsze wersje są z czasem przerzedzane.", + "enabled": "Zachowuj wcześniejsze wersje plików", + "enabledHelp": "Niezależne od kosza. Po wyłączeniu zapisy nie zachowują nic nowego; istniejące wersje pozostają do wygaśnięcia lub usunięcia.", + "keepAllHours": "Zachowuj wszystkie wersje (godziny)", + "hourlyDays": "Potem jedna na godzinę (dni)", + "dailyDays": "Potem jedna na dzień (dni)", + "maxPerFile": "Maksimum wersji na plik", + "sessionCheckpointMinutes": "Punkt kontrolny biurowy (minuty)", + "tiersHelp": "Po tych progach zachowywana jest jedna wersja na tydzień. Sesja edycji biurowej zostawia jedną wersję i najwyżej jeden punkt kontrolny w tym odstępie. Przypięte wersje nie podlegają przerzedzaniu ani limitowi na plik.", + "invalid": "Sprawdź wartości: 1–720 godzin, 1–365 dni godzinowych, 1–3650 dni dziennych, 1–1000 wersji na plik, punkt kontrolny 1–1440 minut.", + "sharedSpace": "Wersje i kosz dzielą zarezerwowaną przestrzeń każdego woluminu. Gdy jej brakuje, najpierw znikają starsze wersje, potem elementy kosza, potem ostatnia wersja każdego pliku, a na końcu przypięte wersje.", + "environmentNote": "Wartości domyślne pochodzą z VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE i VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "Usunąć udostępnienie?", "deleteShareMessage": "Osoby z tym linkiem nie będą już mogły uzyskać dostępu do udostępnionego elementu." + }, + "trash": { + "title": "Kosz", + "menu": "Kosz", + "subtitle": "Elementy są trwale usuwane {count} dzień po usunięciu. | Elementy są trwale usuwane {count} dni po usunięciu. | Elementy są trwale usuwane {count} dni po usunięciu.", + "empty": "Kosz jest pusty.", + "disabled": "Kosz jest wyłączony: usuwanie jest trwałe. To, co nadal tu jest, można przywrócić.", + "unavailable": "Wolumin niedostępny", + "expiresToday": "Dzisiaj", + "expiresIn": "Za {count} dzień | Za {count} dni | Za {count} dni", + "columns": { + "originalLocation": "Pierwotna lokalizacja", + "deletedBy": "Usunięte przez", + "deletedAt": "Data usunięcia", + "expires": "Trwałe usunięcie", + "modified": "Data modyfikacji" + }, + "location": { + "personal": "Folder osobisty", + "unknown": "Nieznana" + }, + "deletedBy": { + "you": "Ty", + "shareLink": "Link udostępniania", + "unknown": "Nieznany" + }, + "actions": { + "restore": "Przywróć", + "deletePermanently": "Usuń trwale", + "empty": "Opróżnij kosz", + "openLocation": "Otwórz pierwotną lokalizację", + "selectAll": "Zaznacz wszystko", + "select": "Zaznacz {name}", + "open": "Otwórz {name}", + "restoreFolder": "Przywróć cały folder", + "restoreTo": "Przywróć do…" + }, + "confirm": { + "deleteTitle": "Usunąć trwale?", + "deleteMessage": "Ten element zostanie usunięty trwale. Tej operacji nie można cofnąć. | Te {count} elementy zostaną usunięte trwale. Tej operacji nie można cofnąć. | Tych {count} elementów zostanie usuniętych trwale. Tej operacji nie można cofnąć.", + "emptyTitle": "Opróżnić kosz?", + "emptyMessage": "Wszystko w Twoim koszu zostanie usunięte trwale. Tej operacji nie można cofnąć." + }, + "results": { + "restored": "Przywrócono {count} element | Przywrócono {count} elementy | Przywrócono {count} elementów", + "restoredTo": "Przywrócono do {path}", + "renamed": "\"{name}\" przywrócono jako \"{newName}\": nazwa była zajęta.", + "notRestored": "Nie udało się przywrócić {count} elementu | Nie udało się przywrócić {count} elementów | Nie udało się przywrócić {count} elementów", + "deleted": "Trwale usunięto {count} element | Trwale usunięto {count} elementy | Trwale usunięto {count} elementów", + "notDeleted": "Nie udało się usunąć {count} elementu | Nie udało się usunąć {count} elementów | Nie udało się usunąć {count} elementów", + "emptied": "Kosz opróżniony: usunięto {count} element | Kosz opróżniony: usunięto {count} elementy | Kosz opróżniony: usunięto {count} elementów", + "leftBehind": "Nie udało się usunąć {count} elementu i nadal tam jest. | Nie udało się usunąć {count} elementów i nadal tam są. | Nie udało się usunąć {count} elementów i nadal tam są." + }, + "reasons": { + "forbidden": "nie możesz już zapisywać w miejscu, w którym się znajdował.", + "blocked": "w miejscu jego folderu jest teraz coś innego.", + "unavailable": "jego wolumin jest niedostępny.", + "busy": "jest już przetwarzany.", + "lost": "jego zawartość zniknęła.", + "notFound": "nie ma go już w koszu.", + "unknown": "nie udało się go przetworzyć.", + "destinationForbidden": "nie możesz tworzyć elementów w tym folderze.", + "invalidDestination": "ten folder nie może go przyjąć." + }, + "errors": { + "load": "Nie udało się wczytać kosza.", + "action": "Kosz nie mógł tego wykonać.", + "loadFolder": "Nie udało się otworzyć tego folderu." + }, + "browse": { + "breadcrumb": "Położenie w koszu", + "deletedFrom": "Usunięto z {location} dnia {date}", + "hint": "Przywracanie odkłada każdy zaznaczony element na jego miejsce w tym folderze. Reszta pozostaje w koszu.", + "empty": "Ten folder jest pusty.", + "back": "Wróć do kosza" + }, + "contextMenu": { + "label": "Działania dla {name}", + "labelMany": "Działania dla elementów: {count}", + "open": "Otwórz", + "preview": "Podgląd" + }, + "shares": { + "badge": "Udostępnione", + "badgeTitle": "Linki udostępniania ({count}), wyłączone na czas pobytu w koszu", + "chooseTitle": "Co zrobić z linkami udostępniania?", + "chooseMessage": "Do tego, co przywracasz, należały linki udostępniania ({count}). Od usunięcia nie działają.", + "chooseElsewhere": "Po przywróceniu będą wskazywać nowe położenie.", + "keep": "Przywróć linki udostępniania", + "drop": "Usuń linki udostępniania", + "deleteNotice": "Ich linki udostępniania również zostaną trwale usunięte.", + "restored": "Przywrócone linki udostępniania: {count}", + "dropped": "Usunięte linki udostępniania: {count}" + } } } diff --git a/frontend/src/i18n/locales/pt-BR.json b/frontend/src/i18n/locales/pt-BR.json index 18e81b71..18ad20c0 100644 --- a/frontend/src/i18n/locales/pt-BR.json +++ b/frontend/src/i18n/locales/pt-BR.json @@ -391,7 +391,8 @@ "filesThumbnails": "Arquivos e Miniaturas", "security": "Segurança", "accessControl": "Controle de Acesso", - "adminUsers": "Gerenciamento de Usuários" + "adminUsers": "Gerenciamento de Usuários", + "trash": "Lixeira e versões" }, "about": { "subtitle": "Veja as informações de compilação deste aplicativo.", @@ -540,6 +541,80 @@ "oidcProvider": "Provedor OIDC", "oidcLinkedProfile": "Perfil Vinculado", "noOidcProfileMessage": "Nenhum perfil OIDC vinculado a esta conta." + }, + "trash": { + "title": "Lixeira e versões", + "subtitle": "Itens excluídos e versões anteriores dos arquivos ficam em um espaço reservado no próprio volume e são removidos ao fim da retenção ou quando esse espaço é necessário.", + "enabled": "Enviar itens excluídos para a lixeira", + "enabledHelp": "Desativada, excluir apaga permanentemente. Itens que já estão na lixeira continuam expirando.", + "retentionDays": "Retenção (dias)", + "retentionHelp": "De 1 a 3650 dias.", + "maxPercent": "Parte de cada volume (%)", + "maxPercentHelp": "O máximo que a lixeira pode ocupar em um volume, de 1 a 90 %.", + "maxSize": "Limite de tamanho por volume (GiB)", + "maxSizeHelp": "Opcional. Vale o menor entre a parte e o limite.", + "maxSizeNone": "Sem limite", + "invalid": "Verifique os valores: retenção de 1 a 3650 dias, parte de 1 a 90 %, um tamanho positivo ou nenhum.", + "saveFailed": "Não foi possível salvar as configurações.", + "environmentNote": "Os valores padrão vêm de TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT e TRASH_MAX_SIZE.", + "zonesTitle": "Lixeira por volume", + "zonesSubtitle": "Cada volume guarda sua lixeira em uma pasta oculta .nextexplorer na raiz, então excluir nunca copia nada.", + "zonesEmpty": "Nada foi excluído para uma lixeira ainda.", + "zonesFailed": "Não foi possível carregar a lixeira dos volumes.", + "verifyFailed": "Não foi possível executar a verificação.", + "maintenanceFailed": "Não foi possível executar a manutenção.", + "zoneKind": { + "personal": "Pasta pessoal de {name}", + "userVolume": "Volume atribuído {name}" + }, + "available": "Disponível", + "unavailable": { + "missing": "Não montado ou removido", + "replaced": "Outro disco no lugar", + "unreadable": "Ilegível" + }, + "usage": "{used} de {budget}", + "zoneSummary": "{count} item, {used} de {budget} | {count} itens, {used} de {budget}", + "noBudget": "sem limite", + "lastPass": "Última manutenção:", + "lastPassPurged": "{count} removido | {count} removidos", + "never": "ainda não", + "verify": "Verificar", + "verifying": "Verificando…", + "verifyOk": "A lixeira de cada volume está consistente.", + "verifyIssues": "{count} problema encontrado | {count} problemas encontrados", + "runMaintenance": "Executar manutenção agora", + "running": "Em andamento…", + "events": { + "title": "Registro", + "evicted": "\"{name}\" removido antes do fim da retenção para liberar espaço", + "adopted": "\"{name}\" recuperado após uma interrupção", + "lost": "O conteúdo de \"{name}\" estava faltando", + "failed": "\"{name}\" não pôde ser removido", + "breaker": "Itens demais faltando de uma vez: nada foi alterado", + "forgotten": "\"{name}\" esquecido por um administrador", + "versionEvicted": "Uma versão de \"{name}\" removida para liberar espaço", + "versionFailed": "Não foi possível remover uma versão", + "versionLost": "O conteúdo de uma versão estava faltando", + "versionTooLarge": "\"{name}\" é maior que todo o espaço: o que ele substituiu não foi guardado", + "versionsBreaker": "Versões demais faltando de uma vez: nada foi alterado" + }, + "zoneVersions": "{count} versão, {size} | {count} versões, {size}", + "versions": { + "title": "Versões de arquivos", + "subtitle": "Cada salvamento guarda o que substitui, sem copiar, no mesmo espaço reservado da lixeira. Versões antigas são espaçadas com o tempo.", + "enabled": "Guardar versões anteriores dos arquivos", + "enabledHelp": "Independente da lixeira. Desativado, os salvamentos não guardam nada novo; as versões existentes permanecem até expirarem ou serem excluídas.", + "keepAllHours": "Guardar todas as versões (horas)", + "hourlyDays": "Depois uma por hora (dias)", + "dailyDays": "Depois uma por dia (dias)", + "maxPerFile": "Máximo de versões por arquivo", + "sessionCheckpointMinutes": "Ponto de controle do Office (minutos)", + "tiersHelp": "Depois dessas faixas, uma versão por semana é guardada. Uma sessão de edição do Office deixa uma versão, mais um ponto de controle no máximo nesse intervalo. Versões fixadas escapam do espaçamento e do limite por arquivo.", + "invalid": "Verifique os valores: 1 a 720 horas, 1 a 365 dias por hora, 1 a 3650 dias por dia, 1 a 1000 versões por arquivo, um ponto de controle de 1 a 1440 minutos.", + "sharedSpace": "Versões e lixeira dividem o espaço reservado de cada volume. Quando falta espaço, saem primeiro as versões antigas, depois os itens da lixeira, depois a última versão de cada arquivo e, por fim, as versões fixadas.", + "environmentNote": "Os padrões vêm de VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE e VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ "sharedWithAnyone": "Todos", "sharedWithUsers": "{count} usuário | {count} usuários", "expiresNever": "Nunca" + }, + "trash": { + "title": "Lixeira", + "menu": "Lixeira", + "subtitle": "Os itens são excluídos permanentemente {count} dia após serem apagados. | Os itens são excluídos permanentemente {count} dias após serem apagados.", + "empty": "A lixeira está vazia.", + "disabled": "A lixeira está desativada: excluir apaga permanentemente. O que ainda está aqui pode ser restaurado.", + "unavailable": "Volume indisponível", + "expiresToday": "Hoje", + "expiresIn": "Em {count} dia | Em {count} dias", + "columns": { + "originalLocation": "Local original", + "deletedBy": "Excluído por", + "deletedAt": "Excluído em", + "expires": "Remoção", + "modified": "Modificado" + }, + "location": { + "personal": "Pasta pessoal", + "unknown": "Desconhecido" + }, + "deletedBy": { + "you": "Você", + "shareLink": "Link de compartilhamento", + "unknown": "Desconhecido" + }, + "actions": { + "restore": "Restaurar", + "deletePermanently": "Excluir permanentemente", + "empty": "Esvaziar lixeira", + "openLocation": "Abrir local original", + "selectAll": "Selecionar tudo", + "select": "Selecionar {name}", + "open": "Abrir {name}", + "restoreFolder": "Restaurar a pasta inteira", + "restoreTo": "Restaurar em…" + }, + "confirm": { + "deleteTitle": "Excluir permanentemente?", + "deleteMessage": "Este item será excluído permanentemente. Esta ação não pode ser desfeita. | Estes {count} itens serão excluídos permanentemente. Esta ação não pode ser desfeita.", + "emptyTitle": "Esvaziar a lixeira?", + "emptyMessage": "Tudo o que está na sua lixeira será excluído permanentemente. Esta ação não pode ser desfeita." + }, + "results": { + "restored": "{count} item restaurado | {count} itens restaurados", + "restoredTo": "Restaurado em {path}", + "renamed": "\"{name}\" foi restaurado como \"{newName}\": o nome já estava em uso.", + "notRestored": "{count} item não pôde ser restaurado | {count} itens não puderam ser restaurados", + "deleted": "{count} item excluído permanentemente | {count} itens excluídos permanentemente", + "notDeleted": "{count} item não pôde ser excluído | {count} itens não puderam ser excluídos", + "emptied": "Lixeira esvaziada: {count} item removido | Lixeira esvaziada: {count} itens removidos", + "leftBehind": "{count} item não pôde ser removido e continua lá. | {count} itens não puderam ser removidos e continuam lá." + }, + "reasons": { + "forbidden": "você não pode mais gravar onde ele estava.", + "blocked": "algo agora ocupa o lugar da sua pasta.", + "unavailable": "seu volume não está disponível.", + "busy": "já está sendo processado.", + "lost": "seu conteúdo havia desaparecido.", + "notFound": "não está mais na lixeira.", + "unknown": "não foi possível processá-lo.", + "destinationForbidden": "você não pode criar itens nessa pasta.", + "invalidDestination": "essa pasta não pode recebê-lo." + }, + "errors": { + "load": "Não foi possível carregar a lixeira.", + "action": "A lixeira não conseguiu fazer isso.", + "loadFolder": "Não foi possível abrir esta pasta." + }, + "browse": { + "breadcrumb": "Local na lixeira", + "deletedFrom": "Excluído de {location} em {date}", + "hint": "Restaurar devolve cada item selecionado ao seu lugar dentro desta pasta. O restante permanece na lixeira.", + "empty": "Esta pasta está vazia.", + "back": "Voltar para a lixeira" + }, + "contextMenu": { + "label": "Ações para {name}", + "labelMany": "Ações para {count} itens", + "open": "Abrir", + "preview": "Pré-visualizar" + }, + "shares": { + "badge": "Compartilhado", + "badgeTitle": "{count} link de compartilhamento, desativado enquanto está na lixeira | {count} links de compartilhamento, desativados enquanto estão na lixeira", + "chooseTitle": "O que fazer com os links de compartilhamento?", + "chooseMessage": "{count} link de compartilhamento pertencia ao que você está restaurando. Ele não funciona desde a exclusão. | {count} links de compartilhamento pertenciam ao que você está restaurando. Eles não funcionam desde a exclusão.", + "chooseElsewhere": "Recuperados, eles apontarão para o novo local.", + "keep": "Restaurar os links de compartilhamento", + "drop": "Excluir os links de compartilhamento", + "deleteNotice": "Os links de compartilhamento também serão excluídos definitivamente.", + "restored": "{count} link de compartilhamento recuperado | {count} links de compartilhamento recuperados", + "dropped": "{count} link de compartilhamento excluído | {count} links de compartilhamento excluídos" + } } } diff --git a/frontend/src/i18n/locales/ro.json b/frontend/src/i18n/locales/ro.json index 3d40dc99..00ff31fb 100644 --- a/frontend/src/i18n/locales/ro.json +++ b/frontend/src/i18n/locales/ro.json @@ -391,7 +391,8 @@ "filesThumbnails": "Fișiere și miniaturi", "security": "Securitate", "accessControl": "Control acces", - "adminUsers": "Gestionare utilizatori" + "adminUsers": "Gestionare utilizatori", + "trash": "Coș de gunoi și versiuni" }, "about": { "subtitle": "Vezi informațiile de build pentru această aplicație.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Coș de gunoi și versiuni", + "subtitle": "Elementele șterse și versiunile anterioare ale fișierelor sunt păstrate într-un spațiu rezervat pe propriul volum și eliminate la finalul retenției sau când este nevoie de acel spațiu.", + "enabled": "Trimite elementele șterse în coșul de gunoi", + "enabledHelp": "Dezactivat, ștergerea elimină definitiv. Elementele aflate deja în coș expiră în continuare.", + "retentionDays": "Păstrare (zile)", + "retentionHelp": "Între 1 și 3650 de zile.", + "maxPercent": "Cota din fiecare volum (%)", + "maxPercentHelp": "Cel mult cât poate ocupa coșul de gunoi pe un volum, între 1 și 90 %.", + "maxSize": "Limită de dimensiune per volum (GiB)", + "maxSizeHelp": "Opțional. Se aplică cea mai mică dintre cotă și limită.", + "maxSizeNone": "Fără limită", + "invalid": "Verificați valorile: păstrare între 1 și 3650 de zile, cotă între 1 și 90 %, o dimensiune pozitivă sau niciuna.", + "saveFailed": "Setările nu au putut fi salvate.", + "environmentNote": "Valorile implicite provin din TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT și TRASH_MAX_SIZE.", + "zonesTitle": "Coș de gunoi pe volum", + "zonesSubtitle": "Fiecare volum își păstrează coșul de gunoi într-un folder ascuns .nextexplorer la rădăcina sa, așa că ștergerea nu copiază niciodată nimic.", + "zonesEmpty": "Încă nu s-a șters nimic într-un coș de gunoi.", + "zonesFailed": "Coșul de gunoi al volumelor nu a putut fi încărcat.", + "verifyFailed": "Verificarea nu a putut rula.", + "maintenanceFailed": "Întreținerea nu a putut rula.", + "zoneKind": { + "personal": "Folderul personal al lui {name}", + "userVolume": "Volum atribuit {name}" + }, + "available": "Disponibil", + "unavailable": { + "missing": "Nemontat sau eliminat", + "replaced": "Alt disc în locul lui", + "unreadable": "Ilizibil" + }, + "usage": "{used} din {budget}", + "zoneSummary": "{count} element, {used} din {budget} | {count} elemente, {used} din {budget}", + "noBudget": "fără limită", + "lastPass": "Ultima întreținere:", + "lastPassPurged": "{count} eliminat | {count} eliminate", + "never": "încă nu", + "verify": "Verifică", + "verifying": "Se verifică…", + "verifyOk": "Coșul de gunoi al fiecărui volum este consecvent.", + "verifyIssues": "{count} problemă găsită | {count} probleme găsite", + "runMaintenance": "Rulează întreținerea acum", + "running": "În curs…", + "events": { + "title": "Jurnal", + "evicted": "\"{name}\" eliminat înainte de sfârșitul păstrării pentru a elibera spațiu", + "adopted": "\"{name}\" recuperat după o întrerupere", + "lost": "Conținutul lui \"{name}\" lipsea", + "failed": "\"{name}\" nu a putut fi eliminat", + "breaker": "Prea multe elemente lipseau deodată: nu s-a modificat nimic", + "forgotten": "\"{name}\" uitat de un administrator", + "versionEvicted": "O versiune a „{name}” eliminată pentru a elibera spațiu", + "versionFailed": "O versiune nu a putut fi eliminată", + "versionLost": "Conținutul unei versiuni lipsea", + "versionTooLarge": "„{name}” depășește tot spațiul: ce înlocuia nu a fost păstrat", + "versionsBreaker": "Prea multe versiuni lipsesc deodată: nu s-a modificat nimic" + }, + "zoneVersions": "{count} versiune, {size} | {count} versiuni, {size}", + "versions": { + "title": "Versiunile fișierelor", + "subtitle": "Fiecare salvare păstrează ce înlocuiește, fără a copia, în același spațiu rezervat ca și coșul de gunoi. Versiunile vechi sunt rărite în timp.", + "enabled": "Păstrați versiunile anterioare ale fișierelor", + "enabledHelp": "Independent de coșul de gunoi. Dezactivat, salvările nu mai păstrează nimic nou; versiunile existente rămân până expiră sau sunt șterse.", + "keepAllHours": "Păstrați toate versiunile (ore)", + "hourlyDays": "Apoi una pe oră (zile)", + "dailyDays": "Apoi una pe zi (zile)", + "maxPerFile": "Cel mult versiuni per fișier", + "sessionCheckpointMinutes": "Punct de control office (minute)", + "tiersHelp": "După aceste trepte se păstrează o versiune pe săptămână. O sesiune de editare office lasă o versiune, plus un punct de control cel mult la acest interval. Versiunile fixate scapă de rărire și de limita per fișier.", + "invalid": "Verificați valorile: 1–720 ore, 1–365 zile orare, 1–3650 zile zilnice, 1–1000 versiuni per fișier, un punct de control de 1–1440 minute.", + "sharedSpace": "Versiunile și coșul de gunoi împart spațiul rezervat al fiecărui volum. Când nu ajunge, pleacă întâi versiunile vechi, apoi elementele din coș, apoi ultima versiune a fiecărui fișier și, la final, versiunile fixate.", + "environmentNote": "Valorile implicite provin din VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE și VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "Elimini partajarea?", "deleteShareMessage": "Persoanele cu acest link nu vor mai putea accesa elementul partajat." + }, + "trash": { + "title": "Coș de gunoi", + "menu": "Coș de gunoi", + "subtitle": "Elementele sunt șterse definitiv la {count} zi după ștergere. | Elementele sunt șterse definitiv la {count} zile după ștergere.", + "empty": "Coșul de gunoi este gol.", + "disabled": "Coșul de gunoi este dezactivat: ștergerea elimină definitiv. Ce se află încă aici poate fi restaurat.", + "unavailable": "Volum indisponibil", + "expiresToday": "Astăzi", + "expiresIn": "Peste {count} zi | Peste {count} zile", + "columns": { + "originalLocation": "Locația originală", + "deletedBy": "Șters de", + "deletedAt": "Șters la", + "expires": "Eliminare", + "modified": "Modificat" + }, + "location": { + "personal": "Folder personal", + "unknown": "Necunoscut" + }, + "deletedBy": { + "you": "Dvs.", + "shareLink": "Link de partajare", + "unknown": "Necunoscut" + }, + "actions": { + "restore": "Restaurează", + "deletePermanently": "Șterge definitiv", + "empty": "Golește coșul de gunoi", + "openLocation": "Deschide locația originală", + "selectAll": "Selectează tot", + "select": "Selectează {name}", + "open": "Deschide {name}", + "restoreFolder": "Restaurează tot dosarul", + "restoreTo": "Restaurează în…" + }, + "confirm": { + "deleteTitle": "Ștergeți definitiv?", + "deleteMessage": "Acest element va fi șters definitiv. Acțiunea nu poate fi anulată. | Aceste {count} elemente vor fi șterse definitiv. Acțiunea nu poate fi anulată.", + "emptyTitle": "Goliți coșul de gunoi?", + "emptyMessage": "Tot ce se află în coșul dvs. de gunoi va fi șters definitiv. Acțiunea nu poate fi anulată." + }, + "results": { + "restored": "{count} element restaurat | {count} elemente restaurate", + "restoredTo": "Restaurat în {path}", + "renamed": "\"{name}\" a fost restaurat ca \"{newName}\": numele era ocupat.", + "notRestored": "{count} element nu a putut fi restaurat | {count} elemente nu au putut fi restaurate", + "deleted": "{count} element șters definitiv | {count} elemente șterse definitiv", + "notDeleted": "{count} element nu a putut fi șters | {count} elemente nu au putut fi șterse", + "emptied": "Coș de gunoi golit: {count} element eliminat | Coș de gunoi golit: {count} elemente eliminate", + "leftBehind": "{count} element nu a putut fi eliminat și este încă acolo. | {count} elemente nu au putut fi eliminate și sunt încă acolo." + }, + "reasons": { + "forbidden": "nu mai puteți scrie unde se afla.", + "blocked": "acum altceva ocupă locul folderului său.", + "unavailable": "volumul său nu este disponibil.", + "busy": "este deja în lucru.", + "lost": "conținutul său dispăruse.", + "notFound": "nu mai este în coșul de gunoi.", + "unknown": "nu a putut fi procesat.", + "destinationForbidden": "nu puteți crea elemente în acel dosar.", + "invalidDestination": "acel dosar nu îl poate primi." + }, + "errors": { + "load": "Coșul de gunoi nu a putut fi încărcat.", + "action": "Coșul de gunoi nu a putut face asta.", + "loadFolder": "Acest dosar nu a putut fi deschis." + }, + "browse": { + "breadcrumb": "Locația în coșul de gunoi", + "deletedFrom": "Șters din {location} la {date}", + "hint": "Restaurează pune fiecare element selectat înapoi la locul lui din acest dosar. Restul rămâne în coșul de gunoi.", + "empty": "Acest dosar este gol.", + "back": "Înapoi la coșul de gunoi" + }, + "contextMenu": { + "label": "Acțiuni pentru {name}", + "labelMany": "Acțiuni pentru {count} elemente", + "open": "Deschide", + "preview": "Previzualizare" + }, + "shares": { + "badge": "Partajat", + "badgeTitle": "{count} link de partajare, dezactivat cât timp este în coșul de gunoi | {count} linkuri de partajare, dezactivate cât timp sunt în coșul de gunoi", + "chooseTitle": "Ce se întâmplă cu linkurile de partajare?", + "chooseMessage": "{count} link de partajare aparținea celor pe care le restaurați. Nu mai funcționează de la ștergere. | {count} linkuri de partajare aparțineau celor pe care le restaurați. Nu mai funcționează de la ștergere.", + "chooseElsewhere": "Readuse, vor indica noua locație.", + "keep": "Restaurează linkurile de partajare", + "drop": "Șterge linkurile de partajare", + "deleteNotice": "Linkurile lor de partajare vor fi și ele șterse definitiv.", + "restored": "{count} link de partajare readus | {count} linkuri de partajare readuse", + "dropped": "{count} link de partajare șters | {count} linkuri de partajare șterse" + } } } diff --git a/frontend/src/i18n/locales/ru.json b/frontend/src/i18n/locales/ru.json index 1f498abb..2c7df246 100644 --- a/frontend/src/i18n/locales/ru.json +++ b/frontend/src/i18n/locales/ru.json @@ -391,7 +391,8 @@ "filesThumbnails": "Файлы и миниатюры", "security": "Безопасность", "accessControl": "Контроль доступа", - "adminUsers": "Управление пользователями" + "adminUsers": "Управление пользователями", + "trash": "Корзина и версии" }, "about": { "subtitle": "Информация о сборке приложения.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Корзина и версии", + "subtitle": "Удалённые элементы и предыдущие версии файлов хранятся в зарезервированном месте на своём томе и удаляются по истечении срока хранения или когда это место нужно.", + "enabled": "Перемещать удалённые элементы в корзину", + "enabledHelp": "Если отключено, удаление происходит навсегда. Элементы, уже находящиеся в корзине, по-прежнему истекают.", + "retentionDays": "Срок хранения (дней)", + "retentionHelp": "От 1 до 3650 дней.", + "maxPercent": "Доля каждого тома (%)", + "maxPercentHelp": "Максимум, который корзина может занимать на томе, от 1 до 90 %.", + "maxSize": "Предел размера на том (ГиБ)", + "maxSizeHelp": "Необязательно. Применяется меньшее из доли и предела.", + "maxSizeNone": "Без предела", + "invalid": "Проверьте значения: срок хранения от 1 до 3650 дней, доля от 1 до 90 %, положительный размер или пусто.", + "saveFailed": "Не удалось сохранить настройки.", + "environmentNote": "Значения по умолчанию берутся из TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT и TRASH_MAX_SIZE.", + "zonesTitle": "Корзина по томам", + "zonesSubtitle": "Каждый том хранит свою корзину в скрытой папке .nextexplorer в корне, поэтому удаление никогда ничего не копирует.", + "zonesEmpty": "В корзину пока ничего не удалялось.", + "zonesFailed": "Не удалось загрузить корзину томов.", + "verifyFailed": "Не удалось выполнить проверку.", + "maintenanceFailed": "Не удалось выполнить обслуживание.", + "zoneKind": { + "personal": "Личная папка {name}", + "userVolume": "Назначенный том {name}" + }, + "available": "Доступен", + "unavailable": { + "missing": "Не смонтирован или удалён", + "replaced": "На его месте другой диск", + "unreadable": "Не читается" + }, + "usage": "{used} из {budget}", + "zoneSummary": "{count} элемент, {used} из {budget} | {count} элемента, {used} из {budget} | {count} элементов, {used} из {budget}", + "noBudget": "без предела", + "lastPass": "Последнее обслуживание:", + "lastPassPurged": "удалено: {count} | удалено: {count} | удалено: {count}", + "never": "ещё не было", + "verify": "Проверить", + "verifying": "Проверка…", + "verifyOk": "Корзина каждого тома согласована.", + "verifyIssues": "Найдена {count} проблема | Найдено {count} проблемы | Найдено {count} проблем", + "runMaintenance": "Запустить обслуживание сейчас", + "running": "Выполняется…", + "events": { + "title": "Журнал", + "evicted": "\"{name}\" удалён до окончания срока хранения, чтобы освободить место", + "adopted": "\"{name}\" восстановлен после прерывания", + "lost": "Содержимое \"{name}\" отсутствовало", + "failed": "Не удалось удалить \"{name}\"", + "breaker": "Слишком много элементов пропало сразу: ничего не изменено", + "forgotten": "\"{name}\" забыт администратором", + "versionEvicted": "Версия «{name}» удалена, чтобы освободить место", + "versionFailed": "Не удалось удалить версию", + "versionLost": "Отсутствовало содержимое версии", + "versionTooLarge": "«{name}» больше всего места: заменённое содержимое не сохранено", + "versionsBreaker": "Слишком много версий пропало сразу: ничего не изменено" + }, + "zoneVersions": "{count} версия, {size} | {count} версии, {size} | {count} версий, {size}", + "versions": { + "title": "Версии файлов", + "subtitle": "Каждое сохранение оставляет то, что заменяет, без копирования, в том же зарезервированном месте, что и корзина. Старые версии со временем прореживаются.", + "enabled": "Хранить предыдущие версии файлов", + "enabledHelp": "Не зависит от корзины. Если выключено, сохранения ничего нового не оставляют; существующие версии хранятся до истечения срока или удаления.", + "keepAllHours": "Хранить все версии (часы)", + "hourlyDays": "Затем одну в час (дни)", + "dailyDays": "Затем одну в день (дни)", + "maxPerFile": "Не больше версий на файл", + "sessionCheckpointMinutes": "Контрольная точка офиса (минуты)", + "tiersHelp": "После этих ступеней хранится одна версия в неделю. Сеанс редактирования в офисном редакторе оставляет одну версию и не чаще этого интервала контрольную точку. Закреплённые версии не прореживаются и не учитываются в лимите на файл.", + "invalid": "Проверьте значения: 1–720 часов, 1–365 дней по часу, 1–3650 дней по дню, 1–1000 версий на файл, контрольная точка 1–1440 минут.", + "sharedSpace": "Версии и корзина делят зарезервированное место каждого тома. Когда его не хватает, сначала удаляются старые версии, затем элементы корзины, затем последняя версия каждого файла и в последнюю очередь закреплённые версии.", + "environmentNote": "Значения по умолчанию берутся из VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE и VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "Удалить общий доступ?", "deleteShareMessage": "Пользователи с этой ссылкой больше не смогут получить доступ к общему элементу." + }, + "trash": { + "title": "Корзина", + "menu": "Корзина", + "subtitle": "Элементы удаляются навсегда через {count} день после удаления. | Элементы удаляются навсегда через {count} дня после удаления. | Элементы удаляются навсегда через {count} дней после удаления.", + "empty": "Корзина пуста.", + "disabled": "Корзина отключена: удаление происходит навсегда. То, что ещё здесь, можно восстановить.", + "unavailable": "Том недоступен", + "expiresToday": "Сегодня", + "expiresIn": "Через {count} день | Через {count} дня | Через {count} дней", + "columns": { + "originalLocation": "Исходное расположение", + "deletedBy": "Кем удалено", + "deletedAt": "Дата удаления", + "expires": "Окончательное удаление", + "modified": "Дата изменения" + }, + "location": { + "personal": "Личная папка", + "unknown": "Неизвестно" + }, + "deletedBy": { + "you": "Вы", + "shareLink": "Ссылка общего доступа", + "unknown": "Неизвестно" + }, + "actions": { + "restore": "Восстановить", + "deletePermanently": "Удалить навсегда", + "empty": "Очистить корзину", + "openLocation": "Открыть исходное расположение", + "selectAll": "Выбрать все", + "select": "Выбрать {name}", + "open": "Открыть {name}", + "restoreFolder": "Восстановить всю папку", + "restoreTo": "Восстановить в…" + }, + "confirm": { + "deleteTitle": "Удалить навсегда?", + "deleteMessage": "Этот элемент будет удалён навсегда. Это действие нельзя отменить. | Эти {count} элемента будут удалены навсегда. Это действие нельзя отменить. | Эти {count} элементов будут удалены навсегда. Это действие нельзя отменить.", + "emptyTitle": "Очистить корзину?", + "emptyMessage": "Всё содержимое вашей корзины будет удалено навсегда. Это действие нельзя отменить." + }, + "results": { + "restored": "Восстановлен {count} элемент | Восстановлено {count} элемента | Восстановлено {count} элементов", + "restoredTo": "Восстановлено в {path}", + "renamed": "\"{name}\" восстановлен как \"{newName}\": имя было занято.", + "notRestored": "Не удалось восстановить {count} элемент | Не удалось восстановить {count} элемента | Не удалось восстановить {count} элементов", + "deleted": "Удалён навсегда {count} элемент | Удалено навсегда {count} элемента | Удалено навсегда {count} элементов", + "notDeleted": "Не удалось удалить {count} элемент | Не удалось удалить {count} элемента | Не удалось удалить {count} элементов", + "emptied": "Корзина очищена: удалён {count} элемент | Корзина очищена: удалено {count} элемента | Корзина очищена: удалено {count} элементов", + "leftBehind": "Не удалось удалить {count} элемент, он всё ещё там. | Не удалось удалить {count} элемента, они всё ещё там. | Не удалось удалить {count} элементов, они всё ещё там." + }, + "reasons": { + "forbidden": "у вас больше нет права записи в его исходное расположение.", + "blocked": "на месте его папки теперь находится что-то другое.", + "unavailable": "его том недоступен.", + "busy": "он уже обрабатывается.", + "lost": "его содержимое исчезло.", + "notFound": "его больше нет в корзине.", + "unknown": "его не удалось обработать.", + "destinationForbidden": "вы не можете создавать элементы в этой папке.", + "invalidDestination": "эта папка не может его принять." + }, + "errors": { + "load": "Не удалось загрузить корзину.", + "action": "Корзина не смогла это выполнить.", + "loadFolder": "Не удалось открыть эту папку." + }, + "browse": { + "breadcrumb": "Расположение в корзине", + "deletedFrom": "Удалено из {location} {date}", + "hint": "Восстановление возвращает каждый выбранный элемент на его место в этой папке. Остальное остаётся в корзине.", + "empty": "Эта папка пуста.", + "back": "Назад в корзину" + }, + "contextMenu": { + "label": "Действия для {name}", + "labelMany": "Действия для элементов: {count}", + "open": "Открыть", + "preview": "Просмотр" + }, + "shares": { + "badge": "Общий доступ", + "badgeTitle": "Ссылки общего доступа ({count}) отключены, пока элемент в корзине", + "chooseTitle": "Что делать со ссылками общего доступа?", + "chooseMessage": "У восстанавливаемого были ссылки общего доступа ({count}). После удаления они не работают.", + "chooseElsewhere": "Возвращённые ссылки будут указывать на новое расположение.", + "keep": "Вернуть ссылки общего доступа", + "drop": "Удалить ссылки общего доступа", + "deleteNotice": "Их ссылки общего доступа тоже будут удалены навсегда.", + "restored": "Возвращено ссылок общего доступа: {count}", + "dropped": "Удалено ссылок общего доступа: {count}" + } } } diff --git a/frontend/src/i18n/locales/sv.json b/frontend/src/i18n/locales/sv.json index 0912908a..1ebdc001 100644 --- a/frontend/src/i18n/locales/sv.json +++ b/frontend/src/i18n/locales/sv.json @@ -391,7 +391,8 @@ "filesThumbnails": "Filer och miniatyrbilder", "security": "Säkerhet", "accessControl": "Åtkomstkontroll", - "adminUsers": "Användarhantering" + "adminUsers": "Användarhantering", + "trash": "Papperskorg och versioner" }, "about": { "subtitle": "Visa bygginformation för denna applikation.", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "Papperskorg och versioner", + "subtitle": "Borttagna objekt och tidigare versioner av filer sparas i ett reserverat utrymme på sin egen volym och tas bort efter lagringstiden eller när utrymmet behövs.", + "enabled": "Flytta raderade objekt till papperskorgen", + "enabledHelp": "Avstängd tar radering bort permanent. Objekt som redan ligger i papperskorgen löper fortfarande ut.", + "retentionDays": "Lagringstid (dagar)", + "retentionHelp": "Från 1 till 3650 dagar.", + "maxPercent": "Andel av varje volym (%)", + "maxPercentHelp": "Det mesta papperskorgen får ta på en volym, från 1 till 90 %.", + "maxSize": "Storleksgräns per volym (GiB)", + "maxSizeHelp": "Valfritt. Det minsta av andelen och gränsen gäller.", + "maxSizeNone": "Ingen gräns", + "invalid": "Kontrollera värdena: lagringstid 1 till 3650 dagar, andel 1 till 90 %, en positiv storlek eller ingen.", + "saveFailed": "Inställningarna kunde inte sparas.", + "environmentNote": "Standardvärdena kommer från TRASH_ENABLED, TRASH_RETENTION_DAYS, TRASH_MAX_PERCENT och TRASH_MAX_SIZE.", + "zonesTitle": "Papperskorg per volym", + "zonesSubtitle": "Varje volym har sin papperskorg i en dold mapp .nextexplorer i roten, så radering kopierar aldrig något.", + "zonesEmpty": "Inget har raderats till en papperskorg ännu.", + "zonesFailed": "Volymernas papperskorg kunde inte läsas in.", + "verifyFailed": "Kontrollen kunde inte köras.", + "maintenanceFailed": "Underhållet kunde inte köras.", + "zoneKind": { + "personal": "Personlig mapp för {name}", + "userVolume": "Tilldelad volym {name}" + }, + "available": "Tillgänglig", + "unavailable": { + "missing": "Inte monterad eller borttagen", + "replaced": "En annan disk på dess plats", + "unreadable": "Oläsbar" + }, + "usage": "{used} av {budget}", + "zoneSummary": "{count} objekt, {used} av {budget} | {count} objekt, {used} av {budget}", + "noBudget": "ingen gräns", + "lastPass": "Senaste underhåll:", + "lastPassPurged": "{count} borttaget | {count} borttagna", + "never": "inte ännu", + "verify": "Kontrollera", + "verifying": "Kontrollerar…", + "verifyOk": "Varje volyms papperskorg är konsekvent.", + "verifyIssues": "{count} problem hittat | {count} problem hittade", + "runMaintenance": "Kör underhåll nu", + "running": "Pågår…", + "events": { + "title": "Logg", + "evicted": "\"{name}\" togs bort före lagringstidens slut för att frigöra utrymme", + "adopted": "\"{name}\" återhämtades efter ett avbrott", + "lost": "Innehållet i \"{name}\" saknades", + "failed": "\"{name}\" kunde inte tas bort", + "breaker": "För många objekt saknades på en gång: inget ändrades", + "forgotten": "\"{name}\" glömdes av en administratör", + "versionEvicted": "En version av ”{name}” togs bort för att frigöra utrymme", + "versionFailed": "En version kunde inte tas bort", + "versionLost": "En version saknade sitt innehåll", + "versionTooLarge": "”{name}” är större än hela utrymmet: det den ersatte behölls inte", + "versionsBreaker": "För många versioner saknas samtidigt: inget ändrades" + }, + "zoneVersions": "{count} version, {size} | {count} versioner, {size}", + "versions": { + "title": "Filversioner", + "subtitle": "Varje sparning behåller det den ersätter, utan att kopiera, i samma reserverade utrymme som papperskorgen. Äldre versioner glesas ut med tiden.", + "enabled": "Behåll tidigare versioner av filer", + "enabledHelp": "Oberoende av papperskorgen. Avstängt behåller sparningar inget nytt; befintliga versioner finns kvar tills de löper ut eller tas bort.", + "keepAllHours": "Behåll alla versioner (timmar)", + "hourlyDays": "Sedan en per timme (dagar)", + "dailyDays": "Sedan en per dag (dagar)", + "maxPerFile": "Högst antal versioner per fil", + "sessionCheckpointMinutes": "Kontrollpunkt för Office (minuter)", + "tiersHelp": "Efter dessa nivåer behålls en version per vecka. En redigeringssession i Office lämnar en version, plus högst en kontrollpunkt med detta intervall. Fästa versioner undantas från utglesningen och gränsen per fil.", + "invalid": "Kontrollera värdena: 1–720 timmar, 1–365 dagar per timme, 1–3650 dagar per dag, 1–1000 versioner per fil, en kontrollpunkt på 1–1440 minuter.", + "sharedSpace": "Versioner och papperskorgen delar varje volyms reserverade utrymme. När det inte räcker försvinner först äldre versioner, sedan objekt i papperskorgen, sedan varje fils senaste version och sist fästa versioner.", + "environmentNote": "Standardvärden kommer från VERSIONS_ENABLED, VERSIONS_KEEP_ALL_HOURS, VERSIONS_HOURLY_DAYS, VERSIONS_DAILY_DAYS, VERSIONS_MAX_PER_FILE och VERSIONS_SESSION_CHECKPOINT_MINUTES." + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "Ta bort delning?", "deleteShareMessage": "Personer med den här länken kommer inte längre åt det delade objektet." + }, + "trash": { + "title": "Papperskorg", + "menu": "Papperskorg", + "subtitle": "Objekt tas bort permanent {count} dag efter att de raderats. | Objekt tas bort permanent {count} dagar efter att de raderats.", + "empty": "Papperskorgen är tom.", + "disabled": "Papperskorgen är avstängd: radering tar bort permanent. Det som fortfarande finns här kan återställas.", + "unavailable": "Volymen är inte tillgänglig", + "expiresToday": "I dag", + "expiresIn": "Om {count} dag | Om {count} dagar", + "columns": { + "originalLocation": "Ursprunglig plats", + "deletedBy": "Raderad av", + "deletedAt": "Raderad", + "expires": "Tas bort", + "modified": "Ändrad" + }, + "location": { + "personal": "Personlig mapp", + "unknown": "Okänd" + }, + "deletedBy": { + "you": "Du", + "shareLink": "Delningslänk", + "unknown": "Okänd" + }, + "actions": { + "restore": "Återställ", + "deletePermanently": "Radera permanent", + "empty": "Töm papperskorgen", + "openLocation": "Öppna ursprunglig plats", + "selectAll": "Markera alla", + "select": "Markera {name}", + "open": "Öppna {name}", + "restoreFolder": "Återställ hela mappen", + "restoreTo": "Återställ till…" + }, + "confirm": { + "deleteTitle": "Radera permanent?", + "deleteMessage": "Det här objektet tas bort permanent. Åtgärden går inte att ångra. | Dessa {count} objekt tas bort permanent. Åtgärden går inte att ångra.", + "emptyTitle": "Töm papperskorgen?", + "emptyMessage": "Allt i din papperskorg tas bort permanent. Åtgärden går inte att ångra." + }, + "results": { + "restored": "{count} objekt återställt | {count} objekt återställda", + "restoredTo": "Återställt till {path}", + "renamed": "\"{name}\" återställdes som \"{newName}\": namnet var upptaget.", + "notRestored": "{count} objekt kunde inte återställas | {count} objekt kunde inte återställas", + "deleted": "{count} objekt raderat permanent | {count} objekt raderade permanent", + "notDeleted": "{count} objekt kunde inte raderas | {count} objekt kunde inte raderas", + "emptied": "Papperskorgen tömd: {count} objekt borttaget | Papperskorgen tömd: {count} objekt borttagna", + "leftBehind": "{count} objekt kunde inte tas bort och finns kvar. | {count} objekt kunde inte tas bort och finns kvar." + }, + "reasons": { + "forbidden": "du kan inte längre skriva där det låg.", + "blocked": "något står nu där dess mapp låg.", + "unavailable": "dess volym är inte tillgänglig.", + "busy": "det hanteras redan.", + "lost": "dess innehåll hade försvunnit.", + "notFound": "det finns inte längre i papperskorgen.", + "unknown": "det kunde inte hanteras.", + "destinationForbidden": "du kan inte skapa objekt i den mappen.", + "invalidDestination": "den mappen kan inte ta emot det." + }, + "errors": { + "load": "Papperskorgen kunde inte läsas in.", + "action": "Papperskorgen kunde inte göra det.", + "loadFolder": "Mappen kunde inte öppnas." + }, + "browse": { + "breadcrumb": "Plats i papperskorgen", + "deletedFrom": "Raderad från {location} {date}", + "hint": "Återställ lägger tillbaka varje markerat objekt på sin plats i den här mappen. Resten ligger kvar i papperskorgen.", + "empty": "Den här mappen är tom.", + "back": "Tillbaka till papperskorgen" + }, + "contextMenu": { + "label": "Åtgärder för {name}", + "labelMany": "Åtgärder för {count} objekt", + "open": "Öppna", + "preview": "Förhandsgranska" + }, + "shares": { + "badge": "Delad", + "badgeTitle": "{count} delningslänk, avstängd medan den ligger i papperskorgen | {count} delningslänkar, avstängda medan de ligger i papperskorgen", + "chooseTitle": "Vad ska hända med delningslänkarna?", + "chooseMessage": "{count} delningslänk hörde till det du återställer. Den har inte fungerat sedan borttagningen. | {count} delningslänkar hörde till det du återställer. De har inte fungerat sedan borttagningen.", + "chooseElsewhere": "Återställda pekar de på den nya platsen.", + "keep": "Återställ delningslänkarna", + "drop": "Ta bort delningslänkarna", + "deleteNotice": "Deras delningslänkar tas också bort permanent.", + "restored": "{count} delningslänk återställd | {count} delningslänkar återställda", + "dropped": "{count} delningslänk borttagen | {count} delningslänkar borttagna" + } } } diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json index eeca9638..54329945 100644 --- a/frontend/src/i18n/locales/zh-CN.json +++ b/frontend/src/i18n/locales/zh-CN.json @@ -391,7 +391,8 @@ "filesThumbnails": "文件和缩略图", "security": "安全", "accessControl": "访问控制", - "adminUsers": "用户管理" + "adminUsers": "用户管理", + "trash": "回收站和版本" }, "about": { "subtitle": "查看此应用的构建信息。", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "回收站和版本", + "subtitle": "已删除的项目和文件的历史版本保存在其所在卷的预留空间中,在保留期结束或需要该空间时移除。", + "enabled": "将删除的项目移至回收站", + "enabledHelp": "关闭后,删除将永久移除项目。回收站中已有的项目仍会到期。", + "retentionDays": "保留期(天)", + "retentionHelp": "1 至 3650 天。", + "maxPercent": "每个卷的占比(%)", + "maxPercentHelp": "回收站在一个卷上最多可占用的比例,1 至 90%。", + "maxSize": "每个卷的大小上限(GiB)", + "maxSizeHelp": "可选。占比与上限取较小者。", + "maxSizeNone": "无上限", + "invalid": "请检查数值:保留期 1 至 3650 天,占比 1 至 90%,正数大小或留空。", + "saveFailed": "无法保存设置。", + "environmentNote": "默认值来自 TRASH_ENABLED、TRASH_RETENTION_DAYS、TRASH_MAX_PERCENT 和 TRASH_MAX_SIZE。", + "zonesTitle": "各卷的回收站", + "zonesSubtitle": "每个卷把回收站放在其根目录的隐藏文件夹 .nextexplorer 中,因此删除从不复制任何内容。", + "zonesEmpty": "还没有任何内容被删除到回收站。", + "zonesFailed": "无法加载各卷的回收站。", + "verifyFailed": "无法运行检查。", + "maintenanceFailed": "无法运行维护。", + "zoneKind": { + "personal": "{name} 的个人文件夹", + "userVolume": "分配的卷 {name}" + }, + "available": "可用", + "unavailable": { + "missing": "未挂载或已移除", + "replaced": "已被另一块磁盘替代", + "unreadable": "无法读取" + }, + "usage": "{used} / {budget}", + "zoneSummary": "{count} 项,{used} / {budget}", + "noBudget": "无上限", + "lastPass": "上次维护:", + "lastPassPurged": "删除 {count} 项", + "never": "尚未进行", + "verify": "检查", + "verifying": "正在检查…", + "verifyOk": "每个卷的回收站均一致。", + "verifyIssues": "发现 {count} 个问题", + "runMaintenance": "立即运行维护", + "running": "正在运行…", + "events": { + "title": "日志", + "evicted": "为释放空间,\"{name}\" 在保留期结束前被删除", + "adopted": "中断后恢复了 \"{name}\"", + "lost": "\"{name}\" 的内容缺失", + "failed": "无法删除 \"{name}\"", + "breaker": "同时缺失的项目过多:未做任何更改", + "forgotten": "管理员忘记了 \"{name}\"", + "versionEvicted": "为释放空间移除了“{name}”的一个版本", + "versionFailed": "无法移除一个版本", + "versionLost": "一个版本的内容缺失", + "versionTooLarge": "“{name}”大于整个空间:被替换的内容未保留", + "versionsBreaker": "同时缺失的版本过多:未做任何更改" + }, + "zoneVersions": "{count} 个版本,{size}", + "versions": { + "title": "文件版本", + "subtitle": "每次保存都会保留被替换的内容,不复制,存放在与回收站相同的预留空间中。旧版本会随时间逐步稀疏。", + "enabled": "保留文件的历史版本", + "enabledHelp": "独立于回收站。关闭后保存不再保留新内容;已有版本保留到过期或被删除。", + "keepAllHours": "保留所有版本(小时)", + "hourlyDays": "然后每小时一个(天)", + "dailyDays": "然后每天一个(天)", + "maxPerFile": "每个文件最多版本数", + "sessionCheckpointMinutes": "办公文档检查点(分钟)", + "tiersHelp": "超过这些阶段后,每周保留一个版本。一次办公文档编辑会话保留一个版本,检查点不会比此间隔更频繁。已固定的版本不受稀疏和每文件上限影响。", + "invalid": "请检查数值:1–720 小时,每小时 1–365 天,每天 1–3650 天,每个文件 1–1000 个版本,检查点 1–1440 分钟。", + "sharedSpace": "版本与回收站共用每个卷的预留空间。空间不足时,依次移除旧版本、回收站项目、每个文件的最新版本,最后才是已固定的版本。", + "environmentNote": "默认值来自 VERSIONS_ENABLED、VERSIONS_KEEP_ALL_HOURS、VERSIONS_HOURLY_DAYS、VERSIONS_DAILY_DAYS、VERSIONS_MAX_PER_FILE 和 VERSIONS_SESSION_CHECKPOINT_MINUTES。" + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "移除共享?", "deleteShareMessage": "拥有此链接的人员将无法再访问共享项目。" + }, + "trash": { + "title": "回收站", + "menu": "回收站", + "subtitle": "项目在删除 {count} 天后将被永久删除。", + "empty": "回收站为空。", + "disabled": "回收站已关闭:删除会永久移除项目。这里仍有的项目可以还原。", + "unavailable": "卷不可用", + "expiresToday": "今天", + "expiresIn": "{count} 天后", + "columns": { + "originalLocation": "原位置", + "deletedBy": "删除者", + "deletedAt": "删除时间", + "expires": "永久删除", + "modified": "修改时间" + }, + "location": { + "personal": "个人文件夹", + "unknown": "未知" + }, + "deletedBy": { + "you": "你", + "shareLink": "共享链接", + "unknown": "未知" + }, + "actions": { + "restore": "还原", + "deletePermanently": "永久删除", + "empty": "清空回收站", + "openLocation": "打开原位置", + "selectAll": "全选", + "select": "选择 {name}", + "open": "打开 {name}", + "restoreFolder": "还原整个文件夹", + "restoreTo": "还原到…" + }, + "confirm": { + "deleteTitle": "永久删除?", + "deleteMessage": "{count} 项将被永久删除。此操作无法撤销。", + "emptyTitle": "清空回收站?", + "emptyMessage": "回收站中的所有内容都将被永久删除。此操作无法撤销。" + }, + "results": { + "restored": "已还原 {count} 项", + "restoredTo": "已还原到 {path}", + "renamed": "\"{name}\" 已还原为 \"{newName}\":原名称已被占用。", + "notRestored": "{count} 项无法还原", + "deleted": "已永久删除 {count} 项", + "notDeleted": "{count} 项无法删除", + "emptied": "回收站已清空:删除了 {count} 项", + "leftBehind": "{count} 项无法删除,仍在回收站中。" + }, + "reasons": { + "forbidden": "你已无法写入其原位置。", + "blocked": "其文件夹的位置现在已被其他内容占用。", + "unavailable": "其所在卷不可用。", + "busy": "它正在处理中。", + "lost": "其内容已消失。", + "notFound": "它已不在回收站中。", + "unknown": "无法处理。", + "destinationForbidden": "你无法在该文件夹中创建项目。", + "invalidDestination": "该文件夹无法接收它。" + }, + "errors": { + "load": "无法加载回收站。", + "action": "回收站无法执行此操作。", + "loadFolder": "无法打开此文件夹。" + }, + "browse": { + "breadcrumb": "回收站中的位置", + "deletedFrom": "于 {date} 从 {location} 删除", + "hint": "还原会将每个选中的项目放回此文件夹中的原位置,其余内容仍留在回收站。", + "empty": "此文件夹为空。", + "back": "返回回收站" + }, + "contextMenu": { + "label": "{name} 的操作", + "labelMany": "{count} 个项目的操作", + "open": "打开", + "preview": "预览" + }, + "shares": { + "badge": "已共享", + "badgeTitle": "{count} 个共享链接,在回收站中时已停用", + "chooseTitle": "如何处理共享链接?", + "chooseMessage": "你要还原的内容有 {count} 个共享链接,自删除起已失效。", + "chooseElsewhere": "恢复后,链接将指向新位置。", + "keep": "恢复共享链接", + "drop": "删除共享链接", + "deleteNotice": "它们的共享链接也将被永久删除。", + "restored": "已恢复 {count} 个共享链接", + "dropped": "已删除 {count} 个共享链接" + } } } diff --git a/frontend/src/i18n/locales/zh-TW.json b/frontend/src/i18n/locales/zh-TW.json index 297743a6..49fc439e 100644 --- a/frontend/src/i18n/locales/zh-TW.json +++ b/frontend/src/i18n/locales/zh-TW.json @@ -391,7 +391,8 @@ "filesThumbnails": "文件縮圖", "security": "安全", "accessControl": "權限控制", - "adminUsers": "使用者管理" + "adminUsers": "使用者管理", + "trash": "資源回收筒和版本" }, "about": { "subtitle": "查看此應用的構建資訊。", @@ -540,6 +541,80 @@ "oidcProvider": "OIDC Provider", "oidcLinkedProfile": "Linked Profile", "noOidcProfileMessage": "No OIDC profiles linked to this account." + }, + "trash": { + "title": "資源回收筒和版本", + "subtitle": "已刪除的項目和檔案的先前版本保存在其所在磁碟區的保留空間中,在保留期結束或需要該空間時移除。", + "enabled": "將刪除的項目移至資源回收筒", + "enabledHelp": "關閉後,刪除會永久移除項目。資源回收筒中已有的項目仍會到期。", + "retentionDays": "保留期(天)", + "retentionHelp": "1 至 3650 天。", + "maxPercent": "每個磁碟區的比例(%)", + "maxPercentHelp": "資源回收筒在一個磁碟區上最多可佔用的比例,1 至 90%。", + "maxSize": "每個磁碟區的大小上限(GiB)", + "maxSizeHelp": "選填。比例與上限取較小者。", + "maxSizeNone": "無上限", + "invalid": "請檢查數值:保留期 1 至 3650 天,比例 1 至 90%,正數大小或留空。", + "saveFailed": "無法儲存設定。", + "environmentNote": "預設值來自 TRASH_ENABLED、TRASH_RETENTION_DAYS、TRASH_MAX_PERCENT 與 TRASH_MAX_SIZE。", + "zonesTitle": "各磁碟區的資源回收筒", + "zonesSubtitle": "每個磁碟區把資源回收筒放在根目錄的隱藏資料夾 .nextexplorer 中,因此刪除從不複製任何內容。", + "zonesEmpty": "尚未有任何內容被刪除到資源回收筒。", + "zonesFailed": "無法載入各磁碟區的資源回收筒。", + "verifyFailed": "無法執行檢查。", + "maintenanceFailed": "無法執行維護。", + "zoneKind": { + "personal": "{name} 的個人資料夾", + "userVolume": "指派的磁碟區 {name}" + }, + "available": "可用", + "unavailable": { + "missing": "未掛載或已移除", + "replaced": "已被另一顆磁碟取代", + "unreadable": "無法讀取" + }, + "usage": "{used} / {budget}", + "zoneSummary": "{count} 個項目,{used} / {budget}", + "noBudget": "無上限", + "lastPass": "上次維護:", + "lastPassPurged": "刪除 {count} 個", + "never": "尚未進行", + "verify": "檢查", + "verifying": "檢查中…", + "verifyOk": "每個磁碟區的資源回收筒皆一致。", + "verifyIssues": "發現 {count} 個問題", + "runMaintenance": "立即執行維護", + "running": "執行中…", + "events": { + "title": "紀錄", + "evicted": "為釋放空間,\"{name}\" 在保留期結束前被刪除", + "adopted": "中斷後已復原 \"{name}\"", + "lost": "\"{name}\" 的內容遺失", + "failed": "無法刪除 \"{name}\"", + "breaker": "同時遺失的項目過多:未做任何變更", + "forgotten": "管理員已忘記 \"{name}\"", + "versionEvicted": "為釋放空間移除了「{name}」的一個版本", + "versionFailed": "無法移除一個版本", + "versionLost": "一個版本的內容遺失", + "versionTooLarge": "「{name}」大於整個空間:被取代的內容未保留", + "versionsBreaker": "同時遺失的版本過多:未做任何變更" + }, + "zoneVersions": "{count} 個版本,{size}", + "versions": { + "title": "檔案版本", + "subtitle": "每次儲存都會保留被取代的內容,不複製,存放在與資源回收筒相同的保留空間中。舊版本會隨時間逐步稀疏。", + "enabled": "保留檔案的先前版本", + "enabledHelp": "獨立於資源回收筒。關閉後儲存不再保留新內容;現有版本保留到過期或被刪除。", + "keepAllHours": "保留所有版本(小時)", + "hourlyDays": "之後每小時一個(天)", + "dailyDays": "之後每天一個(天)", + "maxPerFile": "每個檔案最多版本數", + "sessionCheckpointMinutes": "Office 檢查點(分鐘)", + "tiersHelp": "超過這些階段後,每週保留一個版本。一次 Office 編輯工作階段保留一個版本,檢查點不會比此間隔更頻繁。已釘選的版本不受稀疏與每檔案上限影響。", + "invalid": "請檢查數值:1–720 小時、每小時 1–365 天、每天 1–3650 天、每個檔案 1–1000 個版本、檢查點 1–1440 分鐘。", + "sharedSpace": "版本與資源回收筒共用每個磁碟區的保留空間。空間不足時,依序移除舊版本、資源回收筒項目、每個檔案的最新版本,最後才是已釘選的版本。", + "environmentNote": "預設值來自 VERSIONS_ENABLED、VERSIONS_KEEP_ALL_HOURS、VERSIONS_HOURLY_DAYS、VERSIONS_DAILY_DAYS、VERSIONS_MAX_PER_FILE 和 VERSIONS_SESSION_CHECKPOINT_MINUTES。" + } } }, "mediaPreview": { @@ -632,5 +707,99 @@ }, "deleteShareTitle": "移除分享?", "deleteShareMessage": "擁有此連結的人將無法再存取分享的項目。" + }, + "trash": { + "title": "資源回收筒", + "menu": "資源回收筒", + "subtitle": "項目會在刪除 {count} 天後被永久刪除。", + "empty": "資源回收筒是空的。", + "disabled": "資源回收筒已關閉:刪除會永久移除項目。這裡仍有的項目可以還原。", + "unavailable": "磁碟區無法使用", + "expiresToday": "今天", + "expiresIn": "{count} 天後", + "columns": { + "originalLocation": "原始位置", + "deletedBy": "刪除者", + "deletedAt": "刪除時間", + "expires": "永久刪除", + "modified": "修改時間" + }, + "location": { + "personal": "個人資料夾", + "unknown": "未知" + }, + "deletedBy": { + "you": "你", + "shareLink": "分享連結", + "unknown": "未知" + }, + "actions": { + "restore": "還原", + "deletePermanently": "永久刪除", + "empty": "清空資源回收筒", + "openLocation": "開啟原始位置", + "selectAll": "全選", + "select": "選取 {name}", + "open": "開啟 {name}", + "restoreFolder": "還原整個資料夾", + "restoreTo": "還原到…" + }, + "confirm": { + "deleteTitle": "永久刪除?", + "deleteMessage": "{count} 個項目將被永久刪除。此動作無法還原。", + "emptyTitle": "清空資源回收筒?", + "emptyMessage": "資源回收筒中的所有內容都將被永久刪除。此動作無法還原。" + }, + "results": { + "restored": "已還原 {count} 個項目", + "restoredTo": "已還原到 {path}", + "renamed": "\"{name}\" 已還原為 \"{newName}\":原名稱已被使用。", + "notRestored": "{count} 個項目無法還原", + "deleted": "已永久刪除 {count} 個項目", + "notDeleted": "{count} 個項目無法刪除", + "emptied": "資源回收筒已清空:刪除了 {count} 個項目", + "leftBehind": "{count} 個項目無法刪除,仍在資源回收筒中。" + }, + "reasons": { + "forbidden": "你已無法寫入其原始位置。", + "blocked": "其資料夾的位置現在已被其他內容佔用。", + "unavailable": "其所在磁碟區無法使用。", + "busy": "它正在處理中。", + "lost": "其內容已消失。", + "notFound": "它已不在資源回收筒中。", + "unknown": "無法處理。", + "destinationForbidden": "你無法在該資料夾中建立項目。", + "invalidDestination": "該資料夾無法接收它。" + }, + "errors": { + "load": "無法載入資源回收筒。", + "action": "資源回收筒無法執行此動作。", + "loadFolder": "無法開啟此資料夾。" + }, + "browse": { + "breadcrumb": "資源回收筒中的位置", + "deletedFrom": "於 {date} 從 {location} 刪除", + "hint": "還原會將每個選取的項目放回此資料夾中的原位置,其餘內容仍留在資源回收筒。", + "empty": "此資料夾是空的。", + "back": "返回資源回收筒" + }, + "contextMenu": { + "label": "{name} 的動作", + "labelMany": "{count} 個項目的動作", + "open": "開啟", + "preview": "預覽" + }, + "shares": { + "badge": "已分享", + "badgeTitle": "{count} 個分享連結,在資源回收筒中時已停用", + "chooseTitle": "如何處理分享連結?", + "chooseMessage": "你要還原的內容有 {count} 個分享連結,自刪除起已失效。", + "chooseElsewhere": "恢復後,連結將指向新位置。", + "keep": "恢復分享連結", + "drop": "刪除分享連結", + "deleteNotice": "它們的分享連結也將永久刪除。", + "restored": "已恢復 {count} 個分享連結", + "dropped": "已刪除 {count} 個分享連結" + } } } diff --git a/frontend/src/layouts/BrowserLayout.vue b/frontend/src/layouts/BrowserLayout.vue index 4644fae0..636baecb 100644 --- a/frontend/src/layouts/BrowserLayout.vue +++ b/frontend/src/layouts/BrowserLayout.vue @@ -5,6 +5,7 @@ import FavMenu from '@/components/FavMenu.vue'; import VolMenu from '@/components/VolMenu.vue'; import TerminalMenu from '@/components/TerminalMenu.vue'; import SharesMenu from '@/components/SharesMenu.vue'; +import TrashMenu from '@/components/TrashMenu.vue'; import UploadProgress from '@/components/UploadProgress.vue'; import ClipboardProgress from '@/components/ClipboardProgress.vue'; import UserMenu from '@/components/UserMenu.vue'; @@ -171,6 +172,7 @@ const handleGuestLogin = () => { + diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 3d599476..6bd3fad7 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -13,6 +13,8 @@ import SettingsComingSoon from '@/views/settings/SettingsComingSoon.vue'; import AdminUsers from '@/views/settings/AdminUsers.vue'; import SettingsPassword from '@/views/settings/SettingsPassword.vue'; import SettingsAbout from '@/views/settings/SettingsAbout.vue'; +import SettingsTrash from '@/views/settings/SettingsTrash.vue'; +import TrashView from '@/views/TrashView.vue'; import SettingsUserPreferences from '@/views/settings/SettingsUserPreferences.vue'; import AboutView from '@/views/AboutView.vue'; import AuthSetupView from '@/views/AuthSetupView.vue'; @@ -61,6 +63,11 @@ const router = createRouter({ meta: { requiresAdmin: true }, }, // Admin-only placeholder routes + { + path: 'trash', + component: SettingsTrash, + meta: { requiresAdmin: true }, + }, { path: 'admin-overview', component: SettingsComingSoon, @@ -112,6 +119,12 @@ const router = createRouter({ }, ], }, + { + path: '/trash', + component: BrowserLayout, + meta: { requiresAuth: true }, + children: [{ path: '', name: 'Trash', component: TrashView }], + }, { path: '/shares', component: BrowserLayout, diff --git a/frontend/src/stores/appSettings.js b/frontend/src/stores/appSettings.js index 2df0570a..a18826b3 100644 --- a/frontend/src/stores/appSettings.js +++ b/frontend/src/stores/appSettings.js @@ -31,6 +31,11 @@ export const useAppSettings = defineStore('appSettings', () => { const systemSettings = ref({ thumbnails: { enabled: true, size: 200, quality: 70 }, access: { rules: [] }, + // What the server answers for the trash and the versions. Held as it comes: + // the screen that shows them sends back what it was given, and the server + // is the one that decides what a value may be. + trash: null, + versions: null, }); // Computed state that combines all settings (for backward compatibility) @@ -39,6 +44,8 @@ export const useAppSettings = defineStore('appSettings', () => { user: userSettings.value, thumbnails: systemSettings.value.thumbnails, access: systemSettings.value.access, + trash: systemSettings.value.trash, + versions: systemSettings.value.versions, })); // Whether thumbnails should be shown/requested for the current session. @@ -119,6 +126,8 @@ export const useAppSettings = defineStore('appSettings', () => { ...s.thumbnails, }; } + if (s?.trash) systemSettings.value.trash = { ...s.trash }; + if (s?.versions) systemSettings.value.versions = { ...s.versions }; if (s?.access) { systemSettings.value.access = { rules: Array.isArray(s.access.rules) ? s.access.rules : [], @@ -177,6 +186,8 @@ export const useAppSettings = defineStore('appSettings', () => { }; } + if (updated?.trash) systemSettings.value.trash = { ...updated.trash }; + if (updated?.versions) systemSettings.value.versions = { ...updated.versions }; if (updated?.thumbnails) { systemSettings.value.thumbnails = { enabled: true, diff --git a/frontend/src/stores/features.js b/frontend/src/stores/features.js index e1d451ea..2156523f 100644 --- a/frontend/src/stores/features.js +++ b/frontend/src/stores/features.js @@ -20,6 +20,10 @@ export const useFeaturesStore = defineStore('features', () => { const userVolumesEnabled = ref(false); const skipHome = ref(false); const terminalEnabled = ref(false); + // Whether deleting goes to the trash. The server already says so; nothing + // read it, so the way into the trash could not be shown or hidden. + const trashEnabled = ref(false); + const trashRetentionDays = ref(null); const terminalExtensions = ref([]); const version = ref(''); const gitCommit = ref(''); @@ -82,6 +86,8 @@ export const useFeaturesStore = defineStore('features', () => { // Personal folders personalEnabled.value = Boolean(features?.personal?.enabled); + trashEnabled.value = Boolean(features?.trash?.enabled); + trashRetentionDays.value = features?.trash?.retentionDays ?? null; // User volumes (per-user volume assignments) userVolumesEnabled.value = Boolean(features?.userVolumes?.enabled); @@ -154,6 +160,8 @@ export const useFeaturesStore = defineStore('features', () => { userVolumesEnabled, skipHome, terminalEnabled, + trashEnabled, + trashRetentionDays, terminalExtensions, version, gitCommit, diff --git a/frontend/src/utils/folderRoute.js b/frontend/src/utils/folderRoute.js new file mode 100644 index 00000000..54fd7d59 --- /dev/null +++ b/frontend/src/utils/folderRoute.js @@ -0,0 +1,38 @@ +/** + * Where a folder is, written the way a person would write it. + * + * `router.push({ name: 'FolderView', params: { path } })` hands vue-router a + * single parameter, and vue-router encodes a parameter as one segment: every + * slash inside it comes back as `%2F`, so `/browse/Stacks/data` reaches the + * address bar as `/browse/Stacks%2Fdata`. The route itself has never minded — + * `:path(.+)` matches real slashes — so the only thing between the two shapes + * is how the push is written. The editor has always written it this way. + * + * It is not only about looks. Apache refuses an encoded slash unless + * `AllowEncodedSlashes` is turned on, so a deep link copied from the address + * bar can come back as a 404 from somebody's reverse proxy. + * + * Addresses already saved with `%2F` keep working: they decode to the same + * parameter, and the route resolves them to the same folder. + */ + +/** Each segment encoded on its own, so the slashes between them survive. */ +export const encodeFolderPath = (path) => + String(path ?? '') + .split('/') + .filter(Boolean) + .map(encodeURIComponent) + .join('/'); + +/** + * The route for a folder, ready for `router.push`. + * + * @param {string} path Folder path, `share//…` included. + * @param {object} [query] Left out entirely when empty, so no address grows a + * bare `?`. + */ +export const folderRoute = (path, query) => { + const encoded = encodeFolderPath(path); + const route = { path: encoded ? `/browse/${encoded}` : '/browse/' }; + return query && Object.keys(query).length > 0 ? { ...route, query } : route; +}; diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index f786d0de..d5635cdc 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -4,6 +4,14 @@ function formatDate(unixTimestamp) { return dayjs(unixTimestamp).format('YYYY-MM-DD HH:mm:ss'); } +/** A date and time as the reader's own machine writes them. */ +function formatLocalDateTime(dateString, fallback = '') { + if (!dateString) return fallback; + const date = new Date(dateString); + if (Number.isNaN(date.getTime())) return dateString; + return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; +} + function formatBytes(bytes, decimals) { if (bytes == 0) return '0 Bytes'; var k = 1024, @@ -23,4 +31,4 @@ function withViewTransition(func) { }; } -export { formatDate, formatBytes, withViewTransition }; +export { formatDate, formatLocalDateTime, formatBytes, withViewTransition }; diff --git a/frontend/src/views/TrashView.vue b/frontend/src/views/TrashView.vue new file mode 100644 index 00000000..d79509f8 --- /dev/null +++ b/frontend/src/views/TrashView.vue @@ -0,0 +1,1137 @@ + + + diff --git a/frontend/src/views/settings/SettingsTrash.vue b/frontend/src/views/settings/SettingsTrash.vue new file mode 100644 index 00000000..d8e8ccc8 --- /dev/null +++ b/frontend/src/views/settings/SettingsTrash.vue @@ -0,0 +1,565 @@ + + + diff --git a/frontend/src/views/settings/SettingsView.vue b/frontend/src/views/settings/SettingsView.vue index a5a8c83d..6bb5b338 100644 --- a/frontend/src/views/settings/SettingsView.vue +++ b/frontend/src/views/settings/SettingsView.vue @@ -11,6 +11,7 @@ import { BuildingOfficeIcon, PhotoIcon, KeyIcon, + TrashIcon, UsersIcon, UserCircleIcon, } from '@heroicons/vue/24/outline'; @@ -75,6 +76,12 @@ const adminCategories = [ name: 'Files & Thumbnails', icon: PhotoIcon, }, + { + key: 'trash', + i18nKey: 'trash', + name: 'Trash', + icon: TrashIcon, + }, { key: 'access-control', i18nKey: 'accessControl',