mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
ebc98f0a3f
* fix(plugins): unknown column * fix(mcp): reuse MCP sessions instead of creating one per tool call The /mcp CORS layer never set exposedHeaders, so Access-Control-Expose-Headers was absent and browser-context MCP clients (Claude Desktop connectors, Claude.ai, MCP Inspector) could not read Mcp-Session-Id off the initialize response. Unable to echo it back, every request looked like a fresh initialize: one McpServer and one session per tool call, until the per-user cap returned a 429 and the integration died until the container was restarted. The idle sweep was not at fault — it expires on lastActivity with a 1h default, so sessions born seconds apart are nowhere near expiry, hence 'cleaned 0'. - expose Mcp-Session-Id, MCP-Protocol-Version and WWW-Authenticate - evict a user's least-recently-active session at the cap rather than refusing the request, so a client that cannot persist its session id (or a proxy that strips the header) can never wedge the server - close the McpServer/transport orphaned by every session-less non-initialize POST, which was leaked: never mapped, never swept, never closed - return the cap error as JSON-RPC so clients surface the real reason - warn on session-less POSTs to make a header-stripping proxy diagnosable * chore(deps): declare @modelcontextprotocol/sdk ^1.29.0 Matches the version already resolved in the lockfile; no dependency-tree change. * docs(mcp): add the reverse proxy specs for MCP * fix(plugins): stop the row ⋯ menu being clipped by the sidebar The menu was an in-flow `absolute` div, and its ancestor (PageSidebar) is `overflow-hidden` — which clips absolutely-positioned descendants regardless of z-index. With enough plugins installed a row sits low enough that its menu runs past the sidebar's bottom edge and gets chopped, taking Delete with it, so the plugin could no longer be uninstalled from the UI. Portal the menu to <body> and position it `fixed` against the ⋯ button, flipping upward when the bottom is tight and re-anchoring on scroll/resize. A fixed child of <body> has no overflow ancestor, so nothing can clip it. Closes #1523 * feat(plugins): surface author-signature status and add a scoped re-trust override TREK has always verified an author's Ed25519 signature and TOFU-pinned the key on first install, but none of it was ever shown: a successfully-installed UNSIGNED plugin looked identical to a signed one, and a signature-refused update left the plugin quietly pinned at its old version with the reason dying in a toast. Give the four refusal conditions machine-readable codes (SIGNATURE_MISSING / _INCOMPLETE / _KEY_CHANGED / _INVALID), persist a refusal on the plugin row so the admin list keeps showing it, and badge Signed/Unsigned in the list and in Discover. Only SIGNATURE_KEY_CHANGED is overridable — an author can legitimately rotate a key; a signature that does not verify means the bytes are not what the author signed, and there is no story where waving that through is right. The override re-pins and updates in ONE call (POST :id/retrust): a re-pin that waited for a follow-up /update would leave the plugin pinned to a key no install had ever been verified against if that second call never came. The artifact must still verify under the new key, so a re-trust moves the pin from one verified key to another. assertRetrustable re-derives the condition server-side, so the UI hiding the button is a convenience, not the control, and it echoes back the full key the admin was shown so a re-key since the dialog rendered is refused. The rotation is written to the admin audit log with both fingerprints — after an incident, "which key did we move from, and to what?" is the question a single key cannot answer. * fix(plugins): let a plugin's frame reach the hosts an admin added for it The frame's connect-src was built from the manifest's http:outbound grants alone, but the child's egress guard is the UNION of those and the hosts an admin added after install for an operatorEgress plugin (a self-hosted Gotify, an ntfy — hosts the author cannot know in advance). So such a plugin WITH A UI could call the operator's host from its server and was CSP-blocked in its own iframe. Match the frame to the child. The admin consented to these hosts at install and the child already reaches them, so this widens no trust boundary that isn't already crossed. Both sources stay validated on the way in, and the interpolation filter is unchanged. * fix(plugins): report a rejected events.emit on the plugin's own log stream The host can reject an emit (an undeclared event name, a rate limit). The rejection must not escape — a detached rejection crashes the child and terminally disables the plugin over one bad emit — but swallowing it silently left an author with no way to discover that `emits` was missing from their manifest. Surface it as a warning instead. * feat(plugin-sdk): expose the raw request body for webhook signature checks A webhook author must run their HMAC over the exact bytes the sender signed. `body` is the PARSED value, and re-serializing it will not reproduce those bytes — key order, whitespace and unicode escaping all differ — so the signature never matches. Document `rawBodyBase64`, which the host already sets on auth:false routes. * fix(plugin-sdk): refuse to overwrite a released artifact, and keep the packed zip A released artifact is IMMUTABLE: the registry pins its sha256, so overwriting the bytes of a release already in the registry breaks the checksum for everyone who installed that version — they can no longer install or update it. The old code blanket-caught every `gh release create` failure (auth, network, a bad repo) and turned it into a --clobber upload. Probe for the release explicitly and refuse unless --force. Also stop deleting plugin.zip on the way out. It is the exact bytes the release and the entry's sha256 pin were computed from; a re-pack on another machine or SDK version can differ (CRLF, walk order), so anyone re-running `entry`/`sign` afterwards must hash THAT file, not a rebuild. * fix(plugin-sdk): don't fail submit when the fork already has an upstream remote `gh repo clone` of a fork may already have wired `upstream`, in which case a bare `remote add` exits non-zero and took the whole submit down. Set it either way. * fix(plugin-sdk): make the dev server behave like the host Three ways `dev` lied to an author about how their plugin would run in production: - ctx.db: one try/catch wrapped both the node:sqlite probe AND opening the database, so an mkdir/permission failure silently degraded to the in-memory stub — which swallows every write while reporting success. A db:own plugin "worked" in dev and persisted nothing. Probe separately: fail loudly on a real error, degrade only on old Node, and say plainly that the stub discards writes. - notificationChannel: the host fires it with no acting user and hands the recipient's decrypted settings in as a separate `config` argument — send(msg, config, ctx) / test(config, ctx). Firing it like an ordinary hook passed `ctx` where `config` belongs, so a channel plugin read its settings off ctx and was broken in production. - /preview pinned tripId 42 while the scaffold seeds trip 1, so the widget's first trek:invoke hit assertMember(42) and 500'd. Preview against a trip that exists. * fix(plugin-sdk): reject unknown flags, wire up create's new flags, and add --help `parse()` accepts any --x, so a flag a command does not read was silently dropped: `create --template notification-channel` cheerfully scaffolded a blank plugin. Silently ignoring an author's explicit instruction is worse than refusing it, so unknown flags are now an error, and create actually forwards --template/--egress/--required-addons. A bare --permissions used to split the string "true" into a permission literally named `true`; listFlag() now treats a valueless flag as absent. Since an unknown flag is now fatal, `--help` has to exist: it is intercepted before the flag check and prints usage on stdout with exit 0. * chore(plugins): drop three unused eslint-disable directives The no-console rule is not enabled for these files, so the directives were dead and eslint reports them as unused. * fix(plugin-sdk): bring preflight back in step with the registry's gates preflight exists to tell an author what TREK-Plugins' CI will say before they open the PR. The registry now verifies author signatures, and preflight didn't — so it drifted into the one failure mode it must never have: a false green. An author trusts a green. - Verify the signature against the artifact bytes. preflight already downloads them for the sha256 check, so this costs one call. Without it, signing with the wrong key (or re-packing after signing) sails through and is caught at review. - Check the signature SHAPE (checkSignatureShape): a key with no signed version, a signature with no key, a malformed key or signature. TREK refuses to install a half-signed entry, so such an entry is dead on arrival. - Default apiVersion to 1 before comparing. It is OPTIONAL in the manifest — install/ manifest.ts and `entry` both default it — so a manifest that legally omits it was failing preflight with "manifest apiVersion undefined != entry 1" while the registry passed it. A false RED, which teaches authors to ignore the tool. - Check requiredAddons/pluginDependencies parity, and operatorEgress without an http:outbound grant. The verifier is a port of the host's install/verify-signature.ts (the registry has its own port); sign.ts's verifyArtifact only understands the bare key/signature pair the SDK itself emits and cannot judge a minisign key. A test pins all three to the same verdicts. * feat(plugins): enforce compatibility range * chore: bump sdk version * test(e2e): repair the trip-creation specs create-trip and trip-planner have been failing for a while — long enough for three separate UI changes to drift past them, which nothing caught because CI runs vitest only and never invokes Playwright. Each failure was masking the next: - The release-notice modal greets a freshly seeded user and its backdrop swallowed the click on .add-trip-card. Added a shared dismissSystemNotices() helper (the X only shows on the notice's last page, so it has to page through first). - .modal-backdrop no longer exists — the class was namespaced to .trek-modal-backdrop so content blockers stop hiding it. - input[type=text].first() is no longer the Title field: the cover-image search inputs now sit above it, so the specs were typing the trip name into the photo search box and creating an untitled trip that never matched getByText(title). * fix(planner): gate drag & drop on pointer type, not viewport width (#1432) The 3.2.1 fix disabled drag on "mobile", but nothing in the client has ever detected touch — "mobile" was inferred from viewport width, at four independent breakpoints. A tablet is a coarse-pointer device at a *desktop* width, so an iPad (820-1366px) fell on the wrong side of all four, which is why iPhone was fixed and iPad was not: - useTripPlanner's isMobile (<768px) is what disarmed `draggable`, so on iPad rows stayed draggable and a scroll swipe became an HTML5 drag. - TripPlannerPage hardcoded isMobile={false} on the desktop PlacesSidebar, so its drop handlers and the drop-to-import overlay could never be disabled — that overlay is the reported symptom. - The arrow-button reorder fallback was revealed only below 767px, leaving iPad with no drag *and* no fallback. - touchDragPolyfill loaded drag-drop-touch at >=1024px, synthesising drags from touchmove — on a landscape iPad that re-armed the very gesture hijack 3.2.1 removed. Adds useIsTouch() ((pointer: coarse)) as a signal separate from isMobile: layout stays width-driven, so the iPad keeps the desktop two-pane planner, while every drag affordance is gated on isMobile || isTouch. The reorder arrows now show on coarse pointers, and the polyfill loads only on hybrid laptops ((pointer: fine) and (any-pointer: coarse)), which also removes the #1440 phantom-dblclick map zoom on tablets. Guarded by unit tests plus an e2e spec on real WebKit in an iPad Pro 11 context — the engine matters, since every browser on iPadOS is WebKit underneath, which is why the reporter hit this in all three they tried. * test(planner): guard the day-plan reorder arrows against phantom clicks The hover rule that reveals the reorder arrows had been dead since the TypeScript migration: it targeted `.place-row:hover .reorder-btns`, and neither class exists — the component renders `.reorder-buttons` inside an unclassed row. The buttons sat at opacity:0 on desktop with nothing to reveal them. That was not merely invisible. opacity:0 still hit-tests, so every itinerary row and day note carried an invisible, fully clickable target that silently reordered the trip: opacity: "0", visibility: "visible", pointerEvents: "auto" elementFromPoint(centre) -> BUTTON, inside .reorder-buttons The repair — pointer-events: none while hidden, plus a working .dp-row:hover/:focus-within reveal (focus-within so the buttons are also reachable by keyboard, which the file's JS-hover pattern cannot do) — lives in index.css and DayPlanSidebar.tsx. Both files also carry the #1432 drag gating, so they went with that commit rather than being split mid-file; this commit is the regression guard for them. E2E rather than unit, because jsdom does not evaluate :hover. Against the old CSS it fails on exactly the right assertion: "hidden arrows must not swallow clicks" — expected false, received true. * fix(pdf): keep the header gap on day-header overflow pages (#1531) The repeated <thead> day header (#1471) carried no gap before the first card on overflow pages: the 12px sat in .day-body's block-start padding, which a fragmented box only paints on its first fragment. Move it to the thead cell so it repeats with the header; the day's first page renders pixel-identically. * fix(budget): keep "no one paid yet" when editing an expense (#1533) The ExpenseModal payer initializer fell back to the current user whenever the edited item had no payer, so reopening an expense saved with "no one paid yet" silently reselected "You" — and re-saving then recorded the current user as the payer, corrupting the balances. An absent payer on an existing expense is a deliberate value, so only a brand-new expense defaults to me.
500 lines
31 KiB
TypeScript
500 lines
31 KiB
TypeScript
import type { TranslationStrings } from '../types';
|
|
|
|
const settings: TranslationStrings = {
|
|
'settings.title': 'Impostazioni',
|
|
'settings.subtitle': 'Configura le tue impostazioni personali',
|
|
'settings.tabs.display': 'Visualizzazione',
|
|
'settings.tabs.map': 'Mappa',
|
|
'settings.tabs.notifications': 'Notifiche',
|
|
'settings.tabs.integrations': 'Integrazioni',
|
|
'settings.plugins.oauth.connected': 'Connesso',
|
|
'settings.plugins.oauth.notConnected': 'Non connesso',
|
|
'settings.plugins.oauth.connect': 'Connetti',
|
|
'settings.plugins.oauth.disconnect': 'Disconnetti',
|
|
'settings.tabs.plugins': 'Plugin',
|
|
'settings.plugins.title': 'Impostazioni plugin',
|
|
'settings.plugins.subtitle': 'Le tue impostazioni personali per i plugin che usi (chiavi API, preferenze).',
|
|
'settings.plugins.empty': 'Nessun plugin attivo.',
|
|
'settings.plugins.saved': 'Impostazioni salvate',
|
|
'settings.tabs.account': 'Account',
|
|
'settings.tabs.offline': 'Offline',
|
|
'settings.tabs.about': 'Informazioni',
|
|
'settings.map': 'Mappa',
|
|
'settings.mapTemplate': 'Modello Mappa',
|
|
'settings.mapTemplatePlaceholder.select': 'Seleziona modello...',
|
|
'settings.mapDefaultHint': 'Lascia vuoto per OpenStreetMap (predefinito)',
|
|
'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
'settings.mapHint': 'Modello URL per i tile della mappa',
|
|
'settings.mapProvider': 'Provider mappa',
|
|
'settings.mapProviderHint': 'Influisce sulle mappe Trip Planner e Journey. Atlas usa sempre Leaflet.',
|
|
'settings.mapLeafletSubtitle': 'Classica 2D, qualsiasi tile raster',
|
|
'settings.mapMapboxSubtitle': 'Tile vettoriali, edifici 3D e terreno',
|
|
'settings.mapMapLibreSubtitle': 'Tile vettoriali OpenFreeMap, senza token',
|
|
'settings.mapExperimental': 'Sperimentale',
|
|
'settings.mapMapboxToken': 'Token di accesso Mapbox',
|
|
'settings.mapMapboxTokenHint': 'Token pubblico (pk.*) da',
|
|
'settings.mapMapboxTokenLink': 'mapbox.com → Token di accesso',
|
|
'settings.mapStyle': 'Stile mappa',
|
|
'settings.mapStylePlaceholder': 'Seleziona uno stile Mapbox',
|
|
'settings.mapStyleHint': 'Preset o il tuo URL mapbox://styles/USER/ID',
|
|
'settings.mapOpenFreeMapStylePlaceholder': 'Seleziona uno stile OpenFreeMap',
|
|
'settings.mapOpenFreeMapStyleHint':
|
|
'Preset o URL di stile OpenFreeMap. Gli stili OpenFreeMap funzionano senza token.',
|
|
'settings.map3dBuildings': 'Edifici 3D e terreno',
|
|
'settings.map3dHint':
|
|
'Inclinazione + estrusioni 3D reali degli edifici — funziona con ogni stile, incluso satellite.',
|
|
'settings.mapHighQuality': 'Modalità alta qualità',
|
|
'settings.mapHighQualityHint':
|
|
'Antialiasing + proiezione globo per bordi più nitidi e una vista realistica del mondo.',
|
|
'settings.mapHighQualityWarning': 'Può influire sulle prestazioni su dispositivi meno potenti.',
|
|
'settings.mapTipLabel': 'Suggerimento:',
|
|
'settings.mapTip':
|
|
'Click destro e trascina per ruotare/inclinare la mappa. Click centrale per aggiungere un luogo (il click destro è riservato alla rotazione).',
|
|
'settings.latitude': 'Latitudine',
|
|
'settings.longitude': 'Longitudine',
|
|
'settings.saveMap': 'Salva Mappa',
|
|
'settings.apiKeys': 'Chiavi API',
|
|
'settings.mapsKey': 'Chiave API Google Maps',
|
|
'settings.mapsKeyHint': 'Per la ricerca dei luoghi. Richiede Places API (New). Ottienila su console.cloud.google.com',
|
|
'settings.weatherKey': 'Chiave API OpenWeatherMap',
|
|
'settings.weatherKeyHint': 'Per i dati meteo. Gratuita su openweathermap.org/api',
|
|
'settings.keyPlaceholder': 'Inserisci la chiave...',
|
|
'settings.configured': 'Configurata',
|
|
'settings.saveKeys': 'Salva Chiavi',
|
|
'settings.display': 'Visualizzazione',
|
|
'settings.colorMode': 'Modalità Colore',
|
|
'settings.light': 'Chiara',
|
|
'settings.dark': 'Scura',
|
|
'settings.auto': 'Automatica',
|
|
'settings.language': 'Lingua',
|
|
'settings.temperature': 'Unità di Temperatura',
|
|
'settings.distance': 'Unità di Distanza',
|
|
'settings.timeFormat': 'Formato Ora',
|
|
'settings.blurBookingCodes': 'Nascondi codici di prenotazione',
|
|
'settings.aiAlwaysRetry': 'Always retry booking imports with AI',
|
|
'settings.aiAlwaysRetryHint': 'When a file cannot be read by the standard parser, automatically retry it with AI.',
|
|
'settings.optimizeFromAccommodation': "Ottimizza il percorso dall'alloggio",
|
|
'settings.optimizeFromAccommodationHint':
|
|
"Quando ottimizzi un giorno, fa iniziare il percorso dall'hotel in cui ti svegli e terminarlo in quello in cui fai il check-in quella sera.",
|
|
'settings.notifications': 'Notifiche',
|
|
'settings.notifyTripInvite': 'Inviti di viaggio',
|
|
'settings.notifyBookingChange': 'Modifiche alle prenotazioni',
|
|
'settings.notifyTripReminder': 'Promemoria di viaggio',
|
|
'settings.notifyTodoDue': 'Attività in scadenza',
|
|
'settings.notifyVacayInvite': 'Inviti fusione Vacay',
|
|
'settings.notifyPhotosShared': 'Foto condivise (Immich)',
|
|
'settings.notifyCollabMessage': 'Messaggi chat (Collab)',
|
|
'settings.notifyPackingTagged': 'Lista valigia: assegnazioni',
|
|
'settings.notifyWebhook': 'Notifiche webhook',
|
|
'settings.notificationsDisabled':
|
|
'Le notifiche non sono configurate. Chiedi a un amministratore di abilitare le notifiche e-mail o webhook.',
|
|
'settings.notificationsActive': 'Canale attivo',
|
|
'settings.notificationsManagedByAdmin': "Gli eventi di notifica sono configurati dall'amministratore.",
|
|
'settings.on': 'On',
|
|
'settings.off': 'Off',
|
|
'settings.mcp.title': 'Configurazione MCP',
|
|
'settings.mcp.endpoint': 'Endpoint MCP',
|
|
'settings.mcp.clientConfig': 'Configurazione client',
|
|
'settings.mcp.clientConfigHint':
|
|
'Sostituisci <your_token> con un token API dalla lista sottostante. Il percorso di npx potrebbe dover essere adattato per il tuo sistema (es. C:\\PROGRA~1\\nodejs\\npx.cmd su Windows).',
|
|
'settings.mcp.clientConfigHintOAuth':
|
|
'Replace <your_client_id> and <your_client_secret> with the credentials shown in the OAuth 2.1 client you created above. mcp-remote will open your browser to complete the authorization the first time you connect. The path to npx may need to be adjusted for your system (e.g. C:PROGRA~1\nodejs\npx.cmd on Windows).',
|
|
'settings.mcp.copy': 'Copia',
|
|
'settings.mcp.copied': 'Copiato!',
|
|
'settings.mcp.apiTokens': 'Token API',
|
|
'settings.mcp.createToken': 'Crea nuovo token',
|
|
'settings.mcp.noTokens': 'Nessun token ancora. Creane uno per connettere i client MCP.',
|
|
'settings.mcp.tokenCreatedAt': 'Creato',
|
|
'settings.mcp.tokenUsedAt': 'Utilizzato',
|
|
'settings.mcp.deleteTokenTitle': 'Elimina token',
|
|
'settings.mcp.deleteTokenMessage':
|
|
"Questo token smetterà di funzionare immediatamente. Qualsiasi client MCP che lo utilizza perderà l'accesso.",
|
|
'settings.mcp.modal.createTitle': 'Crea token API',
|
|
'settings.mcp.modal.tokenName': 'Nome del token',
|
|
'settings.mcp.modal.tokenNamePlaceholder': 'es. Claude Desktop, Laptop di lavoro',
|
|
'settings.mcp.modal.creating': 'Creazione…',
|
|
'settings.mcp.modal.create': 'Crea token',
|
|
'settings.mcp.modal.createdTitle': 'Token creato',
|
|
'settings.mcp.modal.createdWarning':
|
|
'Questo token verrà mostrato solo una volta. Copialo e salvalo ora — non può essere recuperato.',
|
|
'settings.mcp.modal.done': 'Fatto',
|
|
'settings.mcp.toast.created': 'Token creato',
|
|
'settings.mcp.toast.createError': 'Impossibile creare il token',
|
|
'settings.mcp.toast.deleted': 'Token eliminato',
|
|
'settings.mcp.toast.deleteError': 'Impossibile eliminare il token',
|
|
'settings.mcp.apiTokensDeprecated':
|
|
'I token API sono deprecati e verranno rimossi in una versione futura. Utilizza invece i client OAuth 2.1.',
|
|
'settings.oauth.clients': 'Client OAuth 2.1',
|
|
'settings.oauth.clientsHint':
|
|
'Registra client OAuth 2.1 per consentire alle applicazioni MCP di terze parti (Claude Web, Cursor, ecc.) di connettersi senza token statici.',
|
|
'settings.oauth.createClient': 'Nuovo client',
|
|
'settings.oauth.noClients': 'Nessun client OAuth registrato.',
|
|
'settings.oauth.clientId': 'ID client',
|
|
'settings.oauth.clientSecret': 'Segreto client',
|
|
'settings.oauth.deleteClient': 'Elimina client',
|
|
'settings.oauth.deleteClientMessage':
|
|
"Questo client e tutte le sessioni attive verranno eliminati definitivamente. Qualsiasi applicazione che lo utilizza perderà immediatamente l'accesso.",
|
|
'settings.oauth.rotateSecret': 'Rinnova segreto',
|
|
'settings.oauth.rotateSecretMessage':
|
|
'Verrà generato un nuovo segreto client e tutte le sessioni esistenti verranno invalidate immediatamente. Aggiorna la tua applicazione prima di chiudere questa finestra.',
|
|
'settings.oauth.rotateSecretConfirm': 'Rinnova',
|
|
'settings.oauth.rotateSecretConfirming': 'Rinnovo in corso…',
|
|
'settings.oauth.rotateSecretDoneTitle': 'Nuovo segreto generato',
|
|
'settings.oauth.rotateSecretDoneWarning':
|
|
'Questo segreto viene mostrato una sola volta. Copialo ora e aggiorna la tua applicazione — tutte le sessioni precedenti sono state invalidate.',
|
|
'settings.oauth.activeSessions': 'Sessioni OAuth attive',
|
|
'settings.oauth.sessionScopes': 'Ambiti',
|
|
'settings.oauth.sessionExpires': 'Scade',
|
|
'settings.oauth.revoke': 'Revoca',
|
|
'settings.oauth.revokeSession': 'Revoca sessione',
|
|
'settings.oauth.revokeSessionMessage': "Questo revocherà immediatamente l'accesso per questa sessione OAuth.",
|
|
'settings.oauth.modal.createTitle': 'Registra client OAuth',
|
|
'settings.oauth.modal.presets': 'Preimpostazioni rapide',
|
|
'settings.oauth.modal.clientName': 'Nome applicazione',
|
|
'settings.oauth.modal.clientNamePlaceholder': 'es. Claude Web, La mia app MCP',
|
|
'settings.oauth.modal.redirectUris': 'URI di reindirizzamento',
|
|
'settings.oauth.modal.redirectUrisPlaceholder': 'https://your-app.com/callback\nhttps://your-app.com/auth',
|
|
'settings.oauth.modal.redirectUrisHint':
|
|
'Un URI per riga. HTTPS richiesto (localhost esente). Corrispondenza esatta richiesta.',
|
|
'settings.oauth.modal.scopes': 'Ambiti consentiti',
|
|
'settings.oauth.modal.scopesHint':
|
|
"list_trips e get_trip_summary sono sempre disponibili — nessun ambito richiesto. Permettono all'IA di scoprire gli ID viaggio necessari.",
|
|
'settings.oauth.modal.selectAll': 'Seleziona tutto',
|
|
'settings.oauth.modal.deselectAll': 'Deseleziona tutto',
|
|
'settings.oauth.modal.creating': 'Registrazione…',
|
|
'settings.oauth.modal.create': 'Registra client',
|
|
'settings.oauth.modal.createdTitle': 'Client registrato',
|
|
'settings.oauth.modal.createdWarning':
|
|
'Il segreto client viene mostrato una sola volta. Copialo ora — non può essere recuperato.',
|
|
'settings.oauth.toast.createError': 'Impossibile registrare il client OAuth',
|
|
'settings.oauth.toast.deleted': 'Client OAuth eliminato',
|
|
'settings.oauth.toast.deleteError': 'Impossibile eliminare il client OAuth',
|
|
'settings.oauth.toast.revoked': 'Sessione revocata',
|
|
'settings.oauth.toast.revokeError': 'Impossibile revocare la sessione',
|
|
'settings.oauth.toast.rotateError': 'Impossibile rinnovare il segreto client',
|
|
'settings.oauth.modal.machineClient': 'Client macchina (senza login nel browser)',
|
|
'settings.oauth.modal.machineClientHint':
|
|
'Usa il grant client_credentials — nessun URI di reindirizzamento necessario. Il token viene emesso direttamente tramite client_id + client_secret e agisce come te negli ambiti selezionati.',
|
|
'settings.oauth.modal.machineClientUsage':
|
|
'Ottieni token: POST /oauth/token con grant_type=client_credentials, client_id e client_secret. Senza browser, senza token di aggiornamento.',
|
|
'settings.oauth.badge.machine': 'macchina',
|
|
'settings.account': 'Account',
|
|
'settings.about': 'Informazioni',
|
|
'settings.about.reportBug': 'Segnala un bug',
|
|
'settings.about.reportBugHint': 'Hai trovato un problema? Faccelo sapere',
|
|
'settings.about.featureRequest': 'Richiedi funzionalità',
|
|
'settings.about.featureRequestHint': 'Suggerisci una nuova funzionalità',
|
|
'settings.about.wikiHint': 'Documentazione e guide',
|
|
'settings.about.supporters.badge': 'Sostenitori Mensili',
|
|
'settings.about.supporters.title': 'Compagni di viaggio per TREK',
|
|
'settings.about.supporters.subtitle':
|
|
'Mentre pianifichi il tuo prossimo itinerario, queste persone aiutano a pianificare il futuro di TREK. Il loro contributo mensile va direttamente allo sviluppo e alle ore realmente investite — per mantenere TREK Open Source.',
|
|
'settings.about.supporters.since': 'sostenitore da {date}',
|
|
'settings.about.supporters.tierEmpty': 'Sii il primo',
|
|
'settings.about.supporter.tier.noReturnTicket': 'No Return Ticket',
|
|
'settings.about.supporter.tier.lostLuggageVip': 'Lost Luggage VIP',
|
|
'settings.about.supporter.tier.businessClassDreamer': 'Business Class Dreamer',
|
|
'settings.about.supporter.tier.budgetTraveller': 'Budget Traveller',
|
|
'settings.about.supporter.tier.hostelBunkmate': 'Hostel Bunkmate',
|
|
'settings.about.description':
|
|
"TREK è un pianificatore di viaggi self-hosted che ti aiuta a organizzare i tuoi viaggi dalla prima idea all'ultimo ricordo. Pianificazione giornaliera, budget, liste bagagli, foto e molto altro — tutto in un unico posto, sul tuo server.",
|
|
'settings.about.madeWith': 'Fatto con',
|
|
'settings.about.madeBy': 'da Maurice e una crescente comunità open-source.',
|
|
'settings.username': 'Nome utente',
|
|
'settings.email': 'Email',
|
|
'settings.role': 'Ruolo',
|
|
'settings.roleAdmin': 'Amministratore',
|
|
'settings.oidcLinked': 'Collegato con',
|
|
'settings.changePassword': 'Cambia Password',
|
|
'settings.currentPassword': 'Password attuale',
|
|
'settings.currentPasswordRequired': 'La password attuale è obbligatoria',
|
|
'settings.newPassword': 'Nuova password',
|
|
'settings.confirmPassword': 'Conferma nuova password',
|
|
'settings.updatePassword': 'Aggiorna password',
|
|
'settings.passwordRequired': 'Inserisci la password attuale e quella nuova',
|
|
'settings.passwordTooShort': 'La password deve contenere almeno 8 caratteri',
|
|
'settings.passwordMismatch': 'Le password non corrispondono',
|
|
'settings.passwordWeak': 'La password deve contenere lettere maiuscole, minuscole, un numero e un carattere speciale',
|
|
'settings.passwordChanged': 'Password cambiata con successo',
|
|
'settings.deleteAccount': 'Elimina account',
|
|
'settings.deleteAccountTitle': 'Eliminare il tuo account?',
|
|
'settings.deleteAccountWarning':
|
|
'Il tuo account e tutti i tuoi viaggi, luoghi e file verranno eliminati in modo permanente. Questa azione non può essere annullata.',
|
|
'settings.deleteAccountConfirm': 'Elimina in modo permanente',
|
|
'settings.deleteBlockedTitle': 'Eliminazione impossibile',
|
|
'settings.deleteBlockedMessage':
|
|
"Sei l'unico amministratore. Promuovi un altro utente ad amministratore prima di eliminare il tuo account.",
|
|
'settings.roleUser': 'Utente',
|
|
'settings.saveProfile': 'Salva Profillo',
|
|
'settings.toast.mapSaved': 'Impostazioni mappa salvate',
|
|
'settings.toast.keysSaved': 'Chiavi API salvate',
|
|
'settings.toast.displaySaved': 'Impostazioni di visualizzazione salvate',
|
|
'settings.toast.profileSaved': 'Profilo salvato',
|
|
'settings.uploadAvatar': 'Carica Immagine del Profilo',
|
|
'settings.removeAvatar': 'Rimuovi Immagine del Profilo',
|
|
'settings.avatarUploaded': 'Immagine del profilo aggiornata',
|
|
'settings.avatarRemoved': 'Immagine del profilo rimossa',
|
|
'settings.avatarError': 'Impossibile caricare',
|
|
'settings.mfa.title': 'Autenticazione a due fattori (2FA)',
|
|
'settings.mfa.description':
|
|
"Aggiunge un secondo passaggio quando accedi con email e password. Usa un'app authenticator (Google Authenticator, Authy, ecc.).",
|
|
'settings.mfa.requiredByPolicy':
|
|
"L'amministratore richiede l'autenticazione a due fattori. Configura un'app authenticator qui sotto prima di continuare.",
|
|
'settings.mfa.backupTitle': 'Codici di backup',
|
|
'settings.mfa.backupDescription': "Usa questi codici monouso se perdi l'accesso alla tua app authenticator.",
|
|
'settings.mfa.backupWarning': 'Salvali adesso. Ogni codice può essere usato una sola volta.',
|
|
'settings.mfa.backupCopy': 'Copia codici',
|
|
'settings.mfa.backupDownload': 'Scarica TXT',
|
|
'settings.mfa.backupPrint': 'Stampa / PDF',
|
|
'settings.mfa.backupCopied': 'Codici di backup copiati',
|
|
'settings.mfa.enabled': 'La 2FA è abilitata sul tuo account.',
|
|
'settings.mfa.disabled': 'La 2FA non è abilitata.',
|
|
'settings.mfa.setup': 'Configura authenticator',
|
|
'settings.mfa.scanQr': 'Scansiona questo codice QR con la tua app, o inserisci il segreto manualmente.',
|
|
'settings.mfa.secretLabel': 'Chiave segreta (inserimento manuale)',
|
|
'settings.mfa.codePlaceholder': 'Codice a 6 cifre',
|
|
'settings.mfa.enable': 'Abilita 2FA',
|
|
'settings.mfa.cancelSetup': 'Annulla',
|
|
'settings.mfa.disableTitle': 'Disabilita 2FA',
|
|
'settings.mfa.disableHint': 'Inserisci la password del tuo account e un codice attuale dal tuo authenticator.',
|
|
'settings.mfa.disable': 'Disabilita 2FA',
|
|
'settings.mfa.toastEnabled': 'Autenticazione a due fattori abilitata',
|
|
'settings.mfa.toastDisabled': 'Autenticazione a due fattori disabilitata',
|
|
'settings.mfa.demoBlocked': 'Non disponibile in modalità demo',
|
|
'settings.mustChangePassword': 'Devi cambiare la password prima di continuare. Imposta una nuova password qui sotto.',
|
|
'settings.bookingLabels': 'Etichette percorsi prenotati',
|
|
'settings.bookingLabelsHint':
|
|
"Mostra i nomi di stazioni / aeroporti sulla mappa. Se disattivato, viene mostrata solo l'icona.",
|
|
'settings.notifyVersionAvailable': 'Nuova versione disponibile',
|
|
'settings.notificationPreferences.noChannels':
|
|
'Nessun canale di notifica configurato. Chiedi a un amministratore di configurare notifiche via e-mail o webhook.',
|
|
'settings.webhookUrl.label': 'URL webhook',
|
|
'settings.webhookUrl.placeholder': 'https://discord.com/api/webhooks/...',
|
|
'settings.webhookUrl.hint': 'Inserisci il tuo URL webhook Discord, Slack o personalizzato per ricevere notifiche.',
|
|
'settings.webhookUrl.saved': 'URL webhook salvato',
|
|
'settings.webhookUrl.test': 'Test',
|
|
'settings.webhookUrl.testSuccess': 'Webhook di test inviato con successo',
|
|
'settings.webhookUrl.testFailed': 'Invio webhook di test fallito',
|
|
'settings.ntfyUrl.topicLabel': 'Argomento Ntfy',
|
|
'settings.ntfyUrl.topicPlaceholder': 'my-trek-alerts',
|
|
'settings.ntfyUrl.serverLabel': 'URL server Ntfy (opzionale)',
|
|
'settings.ntfyUrl.serverPlaceholder': 'https://ntfy.sh',
|
|
'settings.ntfyUrl.hint':
|
|
"Inserisci il tuo argomento Ntfy per ricevere notifiche push. Lascia il server vuoto per usare il valore predefinito configurato dall'amministratore.",
|
|
'settings.ntfyUrl.tokenLabel': 'Token di accesso (opzionale)',
|
|
'settings.ntfyUrl.tokenHint': 'Richiesto per gli argomenti protetti da password.',
|
|
'settings.ntfyUrl.saved': 'Impostazioni Ntfy salvate',
|
|
'settings.ntfyUrl.test': 'Testa',
|
|
'settings.ntfyUrl.testSuccess': 'Notifica di test Ntfy inviata con successo',
|
|
'settings.ntfyUrl.testFailed': 'Notifica di test Ntfy fallita',
|
|
'settings.ntfyUrl.tokenCleared': 'Token di accesso rimosso',
|
|
'settings.notificationPreferences.inapp': 'In-App',
|
|
'settings.notificationPreferences.notConfigured': 'Non ancora configurato — impostalo nelle impostazioni del plugin',
|
|
'settings.plugins.actions': 'Azioni',
|
|
'settings.plugins.actions.confirm': 'Eseguire questa azione?',
|
|
'settings.notificationPreferences.sendTest': 'Invia test',
|
|
'settings.notificationPreferences.configure': 'Configura',
|
|
'settings.notificationPreferences.testSuccess': 'Notifica di prova inviata.',
|
|
'settings.notificationPreferences.testFailed': 'Test non riuscito.',
|
|
'settings.notificationPreferences.pluginConfigured':
|
|
'Configurato. Gestisci le credenziali nelle impostazioni del plugin.',
|
|
'settings.notificationPreferences.webhook': 'Webhook',
|
|
'settings.notificationPreferences.email': 'Email',
|
|
'settings.notificationPreferences.ntfy': 'Ntfy',
|
|
'settings.currency': 'Currency',
|
|
'settings.currencyHint': 'All amounts in Costs are converted to and shown in this currency.',
|
|
'settings.passkey.title': 'Passkey',
|
|
'settings.passkey.description':
|
|
'Accedi più velocemente e in modo resistente al phishing con una passkey — la tua impronta digitale, il volto, il PIN o una chiave hardware. La tua password resta come riserva.',
|
|
'settings.passkey.notConfigured':
|
|
'Le passkey sono abilitate ma non ancora completamente configurate su questo server. Chiedi al tuo amministratore di impostare il dominio WebAuthn.',
|
|
'settings.passkey.add': 'Aggiungi una passkey',
|
|
'settings.passkey.addTitle': 'Aggiungi una passkey',
|
|
'settings.passkey.passwordPrompt': 'Conferma la tua password attuale, poi segui le istruzioni del tuo dispositivo.',
|
|
'settings.passkey.passwordRequired': 'La tua password attuale è obbligatoria.',
|
|
'settings.passkey.namePlaceholder': 'Nome (facoltativo, es. "iPhone")',
|
|
'settings.passkey.addedToast': 'Passkey aggiunta',
|
|
'settings.passkey.added': 'Aggiunta',
|
|
'settings.passkey.addError': 'Impossibile aggiungere la passkey',
|
|
'settings.passkey.cancelled': 'Configurazione della passkey annullata',
|
|
'settings.passkey.deleted': 'Passkey rimossa',
|
|
'settings.passkey.deleteConfirm': 'Rimuovere questa passkey? Conferma con la tua password.',
|
|
'settings.passkey.rename': 'Rinomina',
|
|
'settings.passkey.defaultName': 'Passkey',
|
|
'settings.passkey.synced': 'Sincronizzata',
|
|
'settings.passkey.deviceBound': 'Questo dispositivo',
|
|
'settings.passkey.lastUsed': 'Ultimo utilizzo',
|
|
'settings.passkey.neverUsed': 'Mai usata',
|
|
'settings.mapPoiPill': 'Esplora luoghi sulla mappa',
|
|
'settings.mapPoiPillHint':
|
|
'Mostra un selettore di categorie sulla mappa del viaggio per trovare ristoranti, hotel e altro nelle vicinanze da OpenStreetMap.',
|
|
'settings.airtrail.title': 'AirTrail',
|
|
'settings.airtrail.hint':
|
|
'Collega il tuo AirTrail self-hosted per importare e sincronizzare i voli. Crea una chiave API in AirTrail in Impostazioni → Sicurezza.',
|
|
'settings.airtrail.url': 'URL istanza',
|
|
'settings.airtrail.apiKey': 'Chiave API',
|
|
'settings.airtrail.apiKeyPlaceholder': 'Chiave API Bearer',
|
|
'settings.airtrail.apiKeyHint': 'Generata in AirTrail in Impostazioni → Sicurezza. Memorizzata crittografata.',
|
|
'settings.airtrail.allowInsecureTls': 'Consenti certificati autofirmati',
|
|
'settings.airtrail.allowInsecureTlsHint': "Abilita solo per un'istanza attendibile sulla tua rete.",
|
|
'settings.airtrail.writeBack': 'Scrivi le modifiche su AirTrail',
|
|
'settings.airtrail.writeBackHint':
|
|
'Disattivato per impostazione predefinita: AirTrail è la fonte attendibile e TREK si limita a leggerlo. Attiva per inviare ad AirTrail le modifiche fatte in TREK.',
|
|
'settings.airtrail.connected': 'Connesso',
|
|
'settings.airtrail.notConnected': 'Non connesso',
|
|
'settings.airtrail.toast.saved': 'Connessione AirTrail salvata',
|
|
'settings.airtrail.toast.saveError': 'Impossibile salvare la connessione',
|
|
'settings.airtrail.test.button': 'Prova connessione',
|
|
'settings.airtrail.test.success': 'Connesso — {count} volo/i trovato/i',
|
|
'settings.airtrail.test.failed': 'Connessione fallita',
|
|
'settings.aiParsing.title': 'Analisi AI',
|
|
'settings.aiParsing.hint':
|
|
"Usa il tuo modello AI per estrarre le prenotazioni dai file caricati. Vale solo se l'amministratore non ha configurato un modello per l'intera istanza.",
|
|
'settings.aiParsing.provider': 'Provider',
|
|
'settings.aiParsing.providerLocal': 'Locale (Ollama)',
|
|
'settings.aiParsing.providerOpenai': 'OpenAI',
|
|
'settings.aiParsing.providerAnthropic': 'Anthropic',
|
|
'settings.aiParsing.model': 'Modello',
|
|
'settings.aiParsing.baseUrl': 'URL di base',
|
|
'settings.aiParsing.baseUrlHint':
|
|
'Dove gira il modello: un server Ollama locale o un endpoint compatibile con OpenAI.',
|
|
'settings.aiParsing.apiKey': 'Chiave API',
|
|
'settings.aiParsing.apiKeyHint': 'Memorizzata in forma cifrata. Lascia vuoto per mantenere la chiave attuale.',
|
|
'settings.aiParsing.multimodal': 'Invia i documenti come immagini',
|
|
'settings.aiParsing.multimodalHint':
|
|
'Per i modelli con capacità visive: invia il PDF originale invece del testo estratto.',
|
|
'settings.aiParsing.toast.saved': 'Impostazioni AI salvate',
|
|
'settings.aiParsing.toast.saveError': 'Impossibile salvare le impostazioni AI',
|
|
'settings.tabs.appearance': 'Appearance',
|
|
'settings.appearance.theme': 'Theme',
|
|
'settings.appearance.scheme': 'Color scheme',
|
|
'settings.appearance.scheme.default': 'Default',
|
|
'settings.appearance.scheme.highContrast': 'High contrast',
|
|
'settings.appearance.scheme.indigo': 'Indigo',
|
|
'settings.appearance.scheme.teal': 'Teal',
|
|
'settings.appearance.scheme.rose': 'Rose',
|
|
'settings.appearance.scheme.amber': 'Amber',
|
|
'settings.appearance.scheme.violet': 'Violet',
|
|
'settings.appearance.scheme.custom': 'Custom',
|
|
'settings.appearance.customAccent': 'Custom accent',
|
|
'settings.appearance.contrastOk': 'Good contrast',
|
|
'settings.appearance.contrastLow': 'Low contrast',
|
|
'settings.appearance.readability': 'Readability',
|
|
'settings.appearance.transparency': 'Transparency',
|
|
'settings.appearance.transparencyHint':
|
|
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
|
'settings.appearance.reduceMotion': 'Reduce motion',
|
|
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
|
'settings.appearance.density': 'Density',
|
|
'settings.appearance.comfortable': 'Comfortable',
|
|
'settings.appearance.compact': 'Compact',
|
|
'settings.appearance.textSize': 'Text size',
|
|
'settings.appearance.advancedTextSizes': 'Advanced text sizes',
|
|
'settings.appearance.hideAdvanced': 'Hide advanced',
|
|
'settings.appearance.tier.title': 'Titles',
|
|
'settings.appearance.tier.subtitle': 'Subtitles',
|
|
'settings.appearance.tier.body': 'Body',
|
|
'settings.appearance.tier.caption': 'Captions',
|
|
'settings.appearance.dashboardWidgets': 'Dashboard widgets',
|
|
'settings.appearance.dashboardWidgetsHint': 'Show or hide dashboard widgets independently on desktop and mobile.',
|
|
'settings.appearance.desktop': 'Desktop',
|
|
'settings.appearance.mobile': 'Mobile',
|
|
'settings.appearance.widget.sidebar': 'Right sidebar',
|
|
'settings.appearance.widget.currency': 'Currency',
|
|
'settings.appearance.widget.collections': 'Raccolte',
|
|
'settings.appearance.widget.timezones': 'Timezones',
|
|
'settings.appearance.widget.upcomingReservations': 'Upcoming reservations',
|
|
'settings.appearance.widget.atlas': 'Atlas / countries',
|
|
'settings.appearance.widget.tripsTotal': 'Trips total',
|
|
'settings.appearance.widget.daysTraveled': 'Days traveled',
|
|
'settings.appearance.widget.distanceFlown': 'Distance flown',
|
|
'settings.appearance.reset': 'Reset to defaults',
|
|
'settings.appearance.group.belowHero': 'Below the hero',
|
|
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
|
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
|
'settings.appearance.densityHint':
|
|
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
|
'settings.appearance.textSizeAll': 'Everything',
|
|
'settings.appearance.perSize': 'Adjust each size separately',
|
|
'settings.appearance.size.large': 'Large',
|
|
'settings.appearance.size.medium': 'Medium',
|
|
'settings.appearance.size.normal': 'Normal',
|
|
'settings.appearance.size.small': 'Small',
|
|
'settings.appearance.preview.large': 'Large heading',
|
|
'settings.appearance.preview.medium': 'Medium subtitle',
|
|
'settings.appearance.preview.normal': 'Normal body text',
|
|
'settings.appearance.preview.small': 'Small caption / address',
|
|
'settings.appearance.example.large': 'Headings, big numbers',
|
|
'settings.appearance.example.medium': 'Sub-headings',
|
|
'settings.appearance.example.normal': 'Place names, descriptions',
|
|
'settings.appearance.example.small': 'Addresses, labels',
|
|
'settings.appearance.experimental': 'Experimental',
|
|
'settings.general.languageRegion': 'Language & region',
|
|
'settings.general.travelMap': 'Travel & map',
|
|
|
|
// ── Offline (#1135)
|
|
'settings.offline.cache.title': 'Cache offline',
|
|
'settings.offline.mode.title': 'Modalità offline',
|
|
'settings.offline.mode.force': 'Forza la modalità offline',
|
|
'settings.offline.mode.forceHint':
|
|
'Lavora solo con i dati in cache e metti in coda le tue modifiche. Disattiva per riconnetterti e sincronizzare.',
|
|
'settings.offline.mode.active':
|
|
'La modalità offline è attiva — le tue modifiche sono in coda e verranno sincronizzate quando la disattivi.',
|
|
'settings.offline.prepare.title': "Prepara per l'offline",
|
|
'settings.offline.prepare.hint':
|
|
'Scarica tutto ciò che ti serve — dati del viaggio, documenti e tile della mappa — prima di perdere la connessione.',
|
|
'settings.offline.prepare.button': "Scarica per l'uso offline",
|
|
'settings.offline.prepare.running': 'Download in corso…',
|
|
'settings.offline.prepare.done': "Pronto per l'uso offline",
|
|
'settings.offline.prepare.phase.trips': 'Dati del viaggio',
|
|
'settings.offline.prepare.phase.files': 'Documenti',
|
|
'settings.offline.prepare.phase.tiles': 'Tile della mappa',
|
|
'settings.offline.resync': 'Risincronizza ora',
|
|
'settings.offline.resyncing': 'Sincronizzazione…',
|
|
'settings.offline.storage.title': 'Cosa archiviare offline',
|
|
'settings.offline.storage.tiles': 'Archivia i tile della mappa offline',
|
|
'settings.offline.storage.tilesHint':
|
|
'I tile della mappa usano più spazio di archiviazione. Disattiva questa opzione per conservare offline solo i dati del viaggio e i documenti.',
|
|
'settings.offline.storage.tripsTitle': 'Viaggi',
|
|
'settings.offline.storage.tripOn': 'Archiviato offline',
|
|
'settings.offline.storage.tripOff': 'Non archiviato',
|
|
'settings.offline.clear': 'Svuota la cache',
|
|
'settings.offline.clearConfirm':
|
|
'Cancellare tutti i dati di viaggio offline? Puoi risincronizzare in qualsiasi momento quando sei online.',
|
|
'settings.offline.stats.trips': 'Viaggi in cache',
|
|
'settings.offline.stats.pending': 'Modifiche in sospeso',
|
|
'settings.offline.stats.failed': 'Modifiche non riuscite',
|
|
'settings.offline.stats.conflicts': 'Conflitti',
|
|
'settings.offline.empty': 'Nessun viaggio ancora in cache. Connettiti a internet per sincronizzare.',
|
|
'settings.offline.loading': 'Caricamento…',
|
|
'settings.offline.conflicts.title': 'Conflitti di sincronizzazione',
|
|
'settings.offline.conflicts.hint':
|
|
'Le modifiche fatte offline sono in conflitto con modifiche più recenti sul server. Scegli quale versione mantenere.',
|
|
'settings.offline.conflicts.keepMine': 'Mantieni le mie',
|
|
'settings.offline.conflicts.keepServer': 'Mantieni le loro',
|
|
'settings.offline.conflicts.mine': 'La tua versione',
|
|
'settings.offline.conflicts.server': 'Versione del server',
|
|
'settings.offline.conflicts.item': 'Modifica a “{name}”',
|
|
'settings.offline.conflicts.strategyTitle': 'Quando si verifica un conflitto',
|
|
'settings.offline.conflicts.strategy.ask': 'Chiedimi ogni volta',
|
|
'settings.offline.conflicts.strategy.mine': 'Mantieni sempre la mia versione',
|
|
'settings.offline.conflicts.strategy.server': 'Mantieni sempre la versione del server',
|
|
'settings.offline.banner.offline': 'Offline',
|
|
'settings.offline.banner.forced': 'Modalità offline',
|
|
'settings.offline.banner.queued': 'Offline · {count} in coda',
|
|
'settings.offline.banner.syncing': 'Sincronizzazione di {count}…',
|
|
'settings.offline.banner.failed': 'Sincronizzazione non riuscita: {count}',
|
|
'settings.offline.banner.conflicts': 'Conflitti: {count}',
|
|
'settings.pluginActivity.title': 'Attività dei plugin',
|
|
'settings.pluginActivity.description': 'Ogni azione eseguita da un plugin per tuo conto, dalla più recente.',
|
|
'settings.pluginActivity.empty': 'Nessuna attività dei plugin.',
|
|
'settings.pluginActivity.refresh': 'Aggiorna',
|
|
'settings.pluginActivity.columns.plugin': 'Plugin',
|
|
'settings.pluginActivity.columns.action': 'Azione',
|
|
'settings.pluginActivity.columns.resource': 'Risorsa',
|
|
'settings.pluginActivity.columns.when': 'Quando',
|
|
'settings.pluginActivity.columns.status': 'Esito',
|
|
};
|
|
|
|
export default settings;
|