Files
TREK/shared/src/i18n/fr/admin.ts
T
jubnl ebc98f0a3f v3.4.0 (#1527)
* 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.
2026-07-13 12:40:00 +02:00

679 lines
48 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { TranslationStrings } from '../types';
const admin: TranslationStrings = {
'admin.notifications.title': 'Notifications',
'admin.notifications.hint': 'Choisissez un canal de notification. Un seul peut être actif à la fois.',
'admin.notifications.none': 'Désactivé',
'admin.notifications.email': 'E-mail (SMTP)',
'admin.notifications.webhook': 'Webhook',
'admin.notifications.save': 'Enregistrer les paramètres de notification',
'admin.notifications.saved': 'Paramètres de notification enregistrés',
'admin.notifications.testWebhook': 'Envoyer un webhook de test',
'admin.notifications.testWebhookSuccess': 'Webhook de test envoyé avec succès',
'admin.notifications.testWebhookFailed': 'Échec du webhook de test',
'admin.smtp.title': 'E-mail et notifications',
'admin.smtp.hint': "Configuration SMTP pour l'envoi des notifications par e-mail.",
'admin.smtp.testButton': 'Envoyer un e-mail de test',
'admin.webhook.hint': 'Envoyer des notifications vers un webhook externe (Discord, Slack, etc.).',
'admin.smtp.testSuccess': 'E-mail de test envoyé avec succès',
'admin.smtp.testFailed': "Échec de l'e-mail de test",
'admin.title': 'Administration',
'admin.subtitle': 'Gestion des utilisateurs et paramètres système',
'admin.tabs.users': 'Utilisateurs',
'admin.tabs.categories': 'Catégories',
'admin.tabs.backup': 'Sauvegarde',
'admin.stats.users': 'Utilisateurs',
'admin.stats.trips': 'Voyages',
'admin.stats.places': 'Lieux',
'admin.stats.photos': 'Photos',
'admin.stats.files': 'Fichiers',
'admin.table.user': 'Utilisateur',
'admin.table.email': 'E-mail',
'admin.table.role': 'Rôle',
'admin.table.created': 'Créé le',
'admin.table.lastLogin': 'Dernière connexion',
'admin.table.actions': 'Actions',
'admin.you': '(Vous)',
'admin.editUser': "Modifier l'utilisateur",
'admin.newPassword': 'Nouveau mot de passe',
'admin.newPasswordHint': 'Laissez vide pour conserver le mot de passe actuel',
'admin.deleteUser': "Supprimer l'utilisateur « {name} » ? Tous les voyages seront définitivement supprimés.",
'admin.deleteUserTitle': "Supprimer l'utilisateur",
'admin.newPasswordPlaceholder': 'Saisir le nouveau mot de passe…',
'admin.toast.loadError': "Impossible de charger les données d'administration",
'admin.toast.userUpdated': 'Utilisateur mis à jour',
'admin.toast.updateError': 'Échec de la mise à jour',
'admin.toast.userDeleted': 'Utilisateur supprimé',
'admin.toast.deleteError': 'Échec de la suppression',
'admin.toast.cannotDeleteSelf': 'Impossible de supprimer votre propre compte',
'admin.toast.userCreated': 'Utilisateur créé',
'admin.toast.createError': "Échec de la création de l'utilisateur",
'admin.toast.fieldsRequired': "Le nom d'utilisateur, l'e-mail et le mot de passe sont requis",
'admin.createUser': 'Créer un utilisateur',
'admin.invite.title': "Liens d'invitation",
'admin.invite.subtitle': "Créer des liens d'inscription à usage unique",
'admin.invite.create': 'Créer un lien',
'admin.invite.createAndCopy': 'Créer et copier',
'admin.invite.empty': "Aucun lien d'invitation créé",
'admin.invite.maxUses': 'Utilisations max.',
'admin.invite.expiry': 'Expire après',
'admin.invite.uses': 'utilisé(s)',
'admin.invite.expiresAt': 'expire le',
'admin.invite.createdBy': 'par',
'admin.invite.active': 'Actif',
'admin.invite.expired': 'Expiré',
'admin.invite.usedUp': 'Épuisé',
'admin.invite.copied': "Lien d'invitation copié",
'admin.invite.copyLink': 'Copier le lien',
'admin.invite.deleted': "Lien d'invitation supprimé",
'admin.invite.createError': 'Erreur lors de la création du lien',
'admin.invite.deleteError': 'Erreur lors de la suppression du lien',
'admin.tabs.settings': 'Paramètres',
'admin.allowRegistration': 'Autoriser les inscriptions',
'admin.allowRegistrationHint': "Les nouveaux utilisateurs peuvent s'inscrire eux-mêmes",
'admin.authMethods': 'Authentication Methods',
'admin.passwordLogin': 'Password Login',
'admin.passwordLoginHint': 'Allow users to sign in with email and password',
'admin.passwordRegistration': 'Password Registration',
'admin.passwordRegistrationHint': 'Allow new users to register with email and password',
'admin.oidcLogin': 'SSO Login',
'admin.oidcLoginHint': 'Allow users to sign in with SSO',
'admin.oidcRegistration': 'SSO Auto-Provisioning',
'admin.oidcRegistrationHint': 'Automatically create accounts for new SSO users',
'admin.envOverrideHint':
'Password login settings are controlled by the OIDC_ONLY environment variable and cannot be changed here.',
'admin.lockoutWarning': 'At least one login method must remain enabled',
'admin.requireMfa': "Exiger l'authentification à deux facteurs (2FA)",
'admin.requireMfaHint':
"Les utilisateurs sans 2FA doivent terminer la configuration dans Paramètres avant d'utiliser l'application.",
'admin.apiKeys': 'Clés API',
'admin.apiKeysHint': 'Facultatif. Active les données de lieu étendues comme les photos et la météo.',
'admin.mapsKey': 'Clé API Google Maps',
'admin.mapsKeyHint': 'Requise pour la recherche de lieux. Obtenez-la sur console.cloud.google.com',
'admin.mapsKeyHintLong':
"Sans clé API, OpenStreetMap est utilisé pour la recherche de lieux. Avec une clé Google API, les photos, notes et horaires d'ouverture peuvent également être chargés. Obtenez-en une sur console.cloud.google.com.",
'admin.recommended': 'Recommandé',
'admin.weatherKey': 'Clé API OpenWeatherMap',
'admin.weatherKeyHint': 'Pour les données météo. Gratuit sur openweathermap.org',
'admin.unsplashKey': 'Clé API Unsplash',
'admin.unsplashKeyHint': 'Pour la recherche d’images. Gratuit sur unsplash.com/developers',
'admin.validateKey': 'Tester',
'admin.keyValid': 'Connecté',
'admin.keyInvalid': 'Invalide',
'admin.keySaved': 'Clés API enregistrées',
'admin.oidcTitle': 'Authentification unique (OIDC)',
'admin.oidcSubtitle':
'Autorisez la connexion via des fournisseurs externes comme Google, Apple, Authentik ou Keycloak.',
'admin.oidcDisplayName': "Nom d'affichage",
'admin.oidcIssuer': "URL de l'émetteur",
'admin.oidcIssuerHint': "L'URL de l'émetteur OpenID Connect du fournisseur. ex. https://accounts.google.com",
'admin.oidcSaved': 'Configuration OIDC enregistrée',
'admin.oidcOnlyMode': "Désactiver l'authentification par mot de passe",
'admin.oidcOnlyModeHint':
"Lorsqu'activé, seule la connexion SSO est autorisée. La connexion et l'inscription par mot de passe sont bloquées.",
'admin.fileTypes': 'Types de fichiers autorisés',
'admin.fileTypesHint': 'Configurez les types de fichiers que les utilisateurs peuvent importer.',
'admin.fileTypesFormat':
'Extensions séparées par des virgules (ex. jpg,png,pdf,doc). Utilisez * pour autoriser tous les types.',
'admin.fileTypesSaved': 'Paramètres des types de fichiers enregistrés',
'admin.placesPhotos.title': 'Photos de lieux',
'admin.placesPhotos.subtitle':
"Récupère les photos depuis l'API Google Places. Désactivez pour économiser le quota API. Les photos Wikimedia ne sont pas affectées.",
'admin.placesAutocomplete.title': 'Autocomplétion des lieux',
'admin.placesAutocomplete.subtitle':
"Utilise l'API Google Places pour les suggestions de recherche. Désactivez pour économiser le quota API.",
'admin.placesDetails.title': 'Détails du lieu',
'admin.placesDetails.subtitle':
"Récupère les informations détaillées du lieu (horaires, note, site web) depuis l'API Google Places. Désactivez pour économiser le quota API.",
'admin.bagTracking.title': 'Suivi des bagages',
'admin.bagTracking.subtitle': "Activer le poids et l'attribution de bagages pour les articles",
'admin.collab.chat.title': 'Chat',
'admin.collab.chat.subtitle': 'Messagerie en temps réel pour la collaboration',
'admin.collab.notes.title': 'Notes',
'admin.collab.notes.subtitle': 'Notes et documents partagés',
'admin.collab.polls.title': 'Sondages',
'admin.collab.polls.subtitle': 'Sondages et votes de groupe',
'admin.collab.whatsnext.title': 'Et ensuite',
'admin.collab.whatsnext.subtitle': "Suggestions d'activités et prochaines étapes",
'admin.tabs.config': 'Personnalisation',
'admin.tabs.defaults': 'Valeurs par défaut',
'admin.defaultSettings.title': 'Paramètres utilisateur par défaut',
'admin.defaultSettings.description':
"Définissez des valeurs par défaut pour toute l'instance. Les utilisateurs n'ayant pas modifié un paramètre verront ces valeurs. Leurs propres modifications ont toujours la priorité.",
'admin.defaultSettings.saved': 'Valeur par défaut enregistrée',
'admin.defaultSettings.reset': 'Réinitialiser à la valeur par défaut intégrée',
'admin.defaultSettings.resetToBuiltIn': 'réinitialiser',
'admin.tabs.templates': 'Modèles de bagages',
'admin.packingTemplates.title': 'Modèles de bagages',
'admin.packingTemplates.subtitle': 'Créer des listes de bagages réutilisables pour vos voyages',
'admin.packingTemplates.create': 'Nouveau modèle',
'admin.packingTemplates.namePlaceholder': 'Nom du modèle (ex. Vacances à la plage)',
'admin.packingTemplates.empty': 'Aucun modèle créé',
'admin.packingTemplates.items': 'articles',
'admin.packingTemplates.categories': 'catégories',
'admin.packingTemplates.itemName': "Nom de l'article",
'admin.packingTemplates.itemCategory': 'Catégorie',
'admin.packingTemplates.categoryName': 'Nom de catégorie (ex. Vêtements)',
'admin.packingTemplates.addCategory': 'Ajouter une catégorie',
'admin.packingTemplates.created': 'Modèle créé',
'admin.packingTemplates.deleted': 'Modèle supprimé',
'admin.packingTemplates.loadError': 'Erreur de chargement des modèles',
'admin.packingTemplates.createError': 'Erreur de création du modèle',
'admin.packingTemplates.deleteError': 'Erreur de suppression du modèle',
'admin.packingTemplates.saveError': 'Erreur de sauvegarde',
'admin.tabs.addons': 'Extensions',
'admin.tabs.plugins': 'Plugins',
'admin.plugins.rescan': 'Réanalyser',
'admin.plugins.rescanned': 'Dossier des plugins réanalysé',
'admin.plugins.upload': 'Téléverser un plugin',
'admin.plugins.dropToUpload': "Déposez un .zip de plugin pour l'installer",
'admin.plugins.uploaded': "Plugin “{name}” téléversé — activez-le pour l'exécuter",
'admin.plugins.sideloaded': 'Chargé manuellement',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Chargé depuis un dossier de build local et rechargé à chaud avec des données réelles — dev uniquement',
'admin.plugins.devLinkTitle': 'Lier un plugin local',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Lier',
'admin.plugins.devLinkLinked': "{id} lié — activez-le pour l'exécuter",
'admin.plugins.sideloadedHint': 'Téléversé manuellement — pas depuis le registre, non signé et non vérifié',
'admin.plugins.browse': 'Parcourir les plugins',
'admin.plugins.installed': 'Installé',
'admin.plugins.install': 'Installer',
'admin.plugins.registryEmpty': 'Aucun plugin disponible dans le registre pour le moment.',
'admin.plugins.actionError': "L'action a échoué",
'admin.plugins.activate': 'Activer',
'admin.plugins.activated': 'Plugin activé',
'admin.plugins.deactivate': 'Désactiver',
'admin.plugins.deactivated': 'Plugin désactivé',
'admin.plugins.viewErrors': "Voir le journal d'erreurs",
'admin.plugins.errorLog': "Journal d'erreurs",
'admin.plugins.allowedHosts': 'Hôtes autorisés',
'admin.plugins.allowedHosts.hint':
'Ce plugin communique avec un service que vous seul pouvez nommer (un serveur auto-hébergé). Ajoutez les hôtes qu’il peut joindre — il ne joindra aucun autre.',
'admin.plugins.allowedHosts.none': 'Aucun hôte ajouté pour l’instant.',
'admin.plugins.allowedHosts.unsupported':
'Ce plugin n’utilise pas d’hôtes fournis par l’opérateur. Ses hôtes autorisés sont fixés dans son manifeste.',
'admin.plugins.allowedHosts.restartNote':
'L’enregistrement redémarre le plugin pour qu’il prenne en compte la nouvelle liste.',
'admin.plugins.allowedHosts.add': 'Ajouter un hôte autorisé',
'admin.plugins.allowedHosts.count': '{n} hôte(s) autorisé(s)',
'admin.plugins.operatorEgressPill': '+ hôtes que vous ajoutez',
'admin.plugins.operatorEgressHint':
'Ce plugin communique avec un service que vous seul pouvez nommer (un serveur auto-hébergé). Après installation, ajoutez les hôtes qu’il peut joindre via ⋯ → Hôtes autorisés. Il ne joindra aucun autre.',
'admin.plugins.noErrors': 'Aucune erreur enregistrée.',
'admin.plugins.uninstalled': 'Plugin désinstallé',
'admin.plugins.uninstallTitle': 'Désinstaller le plugin ?',
'admin.plugins.uninstallBody':
'Cela arrête le plugin, supprime son code et efface toutes ses données. Cette action est irréversible.',
'admin.plugins.status.starting': 'Démarrage…',
'admin.plugins.type.widget': 'Widget',
'admin.plugins.type.page': 'Page',
'admin.plugins.type.integration': 'Intégration',
'admin.plugins.type.trip-page': 'Page de voyage',
'admin.plugins.reviewed': 'Vérifié',
'admin.plugins.unreviewed': 'Non vérifié',
'admin.plugins.updated': 'Plugin mis à jour',
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Activer le plugin',
'plugins.notFound': 'Plugin introuvable',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Installez et gérez des plugins tiers sur votre instance.',
'admin.plugins.disabledTitle': 'Les plugins sont désactivés',
'admin.plugins.disabledBody':
"L'environnement d'exécution des plugins est désactivé (TREK_PLUGINS_ENABLED). Aucun plugin ne peut fonctionner tant qu'un administrateur ne l'active pas dans la configuration du serveur.",
'admin.plugins.empty': 'Aucun plugin installé pour le moment.',
'admin.plugins.loadError': 'Impossible de charger les plugins.',
'admin.plugins.scaffoldNote':
"L'installation et l'activation des plugins arriveront dans une mise à jour ultérieure. Ce panneau liste actuellement uniquement les plugins installés.",
'admin.plugins.status.active': 'Actif',
'admin.plugins.status.inactive': 'Inactif',
'admin.plugins.status.disabled': 'Désactivé',
'admin.plugins.status.error': 'Erreur',
'admin.plugins.status.incompatible': 'Incompatible',
'admin.plugins.details': 'Détails',
'admin.plugins.detailError': 'Impossible de charger les détails du plugin.',
'admin.plugins.permissionsTitle': 'Permissions',
'admin.plugins.noPermissions': 'Ce plugin ne demande aucune permission spéciale.',
'admin.plugins.egressNote': 'Peut se connecter à : {hosts}',
'admin.plugins.setupTitle': 'Configuration',
'admin.plugins.noSetup': 'Fonctionne immédiatement — rien à configurer.',
'admin.plugins.scope.instance': "Toute l'instance",
'admin.plugins.scope.user': 'Par utilisateur',
'admin.plugins.fieldRequired': 'Requis',
'admin.plugins.sourceRepo': 'Dépôt source',
'admin.plugins.reportIssue': 'Signaler un problème',
'admin.plugins.homepage': 'Site web',
'admin.plugins.requiresTrek': 'Nécessite TREK {version}+',
'admin.plugins.reviewedOn': 'Vérifié le {date}',
'admin.plugins.perm.db:own': 'Stocker ses propres données dans une base de données isolée',
'admin.plugins.perm.db:read:trips': "Lire les voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:read:users': 'Lire les informations de profil de base (nom, avatar — jamais les identifiants)',
'admin.plugins.perm.db:read:costs': "Lire les dépenses (postes de budget) accessibles à l'utilisateur actif",
'admin.plugins.perm.db:read:packing': "Lire les listes de bagages des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:write:packing':
"Créer, modifier et supprimer des articles de la liste de bagages sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.weather:read': "Lire les prévisions météo en cache de l'hôte (par coordonnées)",
'admin.plugins.perm.db:read:categories': 'Lire la liste globale des catégories de lieux',
'admin.plugins.perm.db:read:tags': "Lire les étiquettes de l'utilisateur actif",
'admin.plugins.perm.db:write:tags': "Créer, modifier et supprimer les étiquettes de l'utilisateur actif",
'admin.plugins.perm.db:read:todos': "Lire les tâches des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:write:todos':
"Créer, modifier et supprimer des tâches sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:atlas':
"Marquer des pays et régions comme visités et gérer la liste d'envies de l'utilisateur actif (nécessite le module Atlas)",
'admin.plugins.perm.db:write:vacay':
"Basculer les jours de congé et les fermetures d'entreprise sur le plan actif de l'utilisateur actif (nécessite le module Vacay)",
'admin.plugins.perm.db:write:journal':
"Créer, modifier et supprimer des entrées de journal dans les journaux que l'utilisateur actif peut modifier (nécessite le module Journey)",
'admin.plugins.perm.db:write:collections':
"Créer et modifier des collections et y enregistrer des lieux, avec le rôle de collection de l'utilisateur actif (nécessite le module Collections)",
'admin.plugins.perm.db:write:files':
"Joindre des fichiers aux voyages que l'utilisateur actif peut modifier (limite de 10 Mo, extensions bloquées refusées) et gérer leurs liens",
'admin.plugins.perm.db:write:collab':
"Publier des notes, des sondages et des messages de chat sur les voyages que l'utilisateur actif peut modifier (nécessite le module Collab)",
'admin.plugins.perm.db:write:members':
"Ajouter des utilisateurs aux voyages — accorde l'accès au voyage ; protégé par le droit de gestion des membres de l'utilisateur actif",
'admin.plugins.perm.notify:send':
"Envoyer une notification (boîte de réception + e-mail/ntfy) à l'utilisateur actif ou à un voyage auquel il appartient — jamais à un destinataire arbitraire",
'admin.plugins.perm.ai:invoke':
"Utiliser le fournisseur d'IA configuré par l'administrateur (complétion de texte + extraction de documents) au nom de l'utilisateur actif — le module ne détient jamais de clé",
'admin.plugins.perm.oauth:client':
"Se connecter à un service tiers en votre nom via OAuth géré par l'hôte (l'hôte détient les jetons, le module ne les voit jamais)",
'admin.plugins.perm.db:read:files': "Lire les fichiers des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:write:reservations':
"Créer, modifier et supprimer des réservations sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:accommodations':
"Créer, modifier et supprimer des hébergements sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:read:journal':
"Lire les journaux de voyage de l'utilisateur actif (nécessite le module Journey)",
'admin.plugins.perm.db:read:atlas':
"Lire les pays et régions visités par l'utilisateur actif (nécessite le module Atlas)",
'admin.plugins.perm.db:read:vacay': "Lire le plan de vacances de l'utilisateur actif (nécessite le module Vacay)",
'admin.plugins.perm.db:read:daynotes': "Lire les notes de journée des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:read:collections':
"Lire les collections de lieux enregistrés de l'utilisateur actif (nécessite le module Collections)",
'admin.plugins.perm.db:write:daynotes':
"Créer, modifier et supprimer des notes de journée sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.jobs:run':
"Exécuter ses tâches d'arrière-plan selon une planification (sans contexte utilisateur — ne peut pas lire les données utilisateur)",
'admin.plugins.perm.db:write:costs':
"Créer des dépenses (postes de budget) sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:places':
"Ajouter, modifier et supprimer des lieux sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:days':
"Ajouter, modifier et supprimer des jours sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:itinerary':
"Attribuer et retirer des lieux aux jours des voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:trips':
"Modifier les détails du voyage (titre, dates, devise…) sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:meta':
"Associer ses propres données privées aux voyages, lieux et jours auxquels l'utilisateur actif a accès",
'admin.plugins.perm.ws:broadcast:trip': 'Envoyer des mises à jour en temps réel aux membres du voyage',
'admin.plugins.perm.ws:broadcast:user': 'Envoyer des mises à jour en temps réel à des utilisateurs individuels',
'admin.plugins.perm.hook:photo-provider': 'Fournir des photos à Photos',
'admin.plugins.perm.hook:calendar-source': 'Fournir des événements au calendrier',
'admin.plugins.perm.hook:place-detail-provider': 'Ajouter des détails supplémentaires (avis, notes, liens) à un lieu',
'admin.plugins.perm.hook:trip-warning-provider':
'Émettre des avertissements de validation sur un voyage (affichés dans le planificateur)',
'admin.plugins.perm.hook:table-contributor':
'Ajouter des colonnes et des actions aux vues du voyage (réservations, lieux, jours)',
'admin.plugins.perm.hook:map-marker-provider':
'Ajouter des marqueurs à la carte du voyage (p. ex. afficher réservations ou POIs)',
'admin.plugins.perm.hook:pdf-section-provider': "Ajouter des sections de texte à l'export PDF du voyage",
'admin.plugins.perm.hook:atlas-layer-provider':
"Mettre en évidence des pays sur la carte du monde Atlas (p. ex. listes d'envies ou avis aux voyageurs)",
'admin.plugins.perm.hook:journal-entry-provider':
'Ajouter des lignes supplémentaires (liens, statistiques) aux entrées du journal',
'admin.plugins.perm.hook:user-data':
"Effacer ou exporter les données qu'il stocke sur un utilisateur (RGPD : suppression de compte et demandes d'accès)",
'admin.plugins.perm.hook:trip-card-provider':
'Ajouter de petits badges (statut, compteurs) aux cartes de voyage du tableau de bord',
'admin.plugins.perm.hook:notification-channel': 'Envoyer vos notifications via un canal supplémentaire',
'admin.plugins.perm.events:subscribe':
'Réagir aux événements d’activité principaux (nom de l’événement et voyage uniquement, jamais le contenu)',
'admin.plugins.perm.http:outbound': 'Effectuer des requêtes sortantes vers ses hôtes déclarés',
'admin.plugins.perm.db:read:collab':
"Lire les notes, sondages et messages de chat des voyages accessibles à l'utilisateur actif (nécessite le module Collab)",
'admin.plugins.perm.db:read:files:content':
"Lire le contenu (octets) des fichiers des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:create:trips': "Créer de nouveaux voyages appartenant à l'utilisateur actif",
'admin.plugins.perm.rates:read': "Lire les taux de change en cache de l'hôte",
'admin.plugins.updateConsentTitle': 'Cette mise à jour nécessite de nouvelles permissions',
'admin.plugins.updateConsentBody':
"{name} v{version} demande des droits que vous n'avez pas encore accordés. La nouvelle version est installée mais reste désactivée jusqu'à ce que vous l'approuviez.",
'admin.plugins.updateNewPermissions': 'Nouvelles permissions demandées',
'admin.plugins.updateNewEgress': 'Nouvelles connexions sortantes',
'admin.plugins.updateApprove': 'Approuver et activer',
'admin.plugins.updateLater': "Laisser désactivé pour l'instant",
'admin.plugins.updateKeptOff':
"Mise à jour installée — laissée désactivée jusqu'à ce que vous approuviez les nouvelles permissions",
'admin.plugins.reviewedMeaning':
"« Vérifié » signifie qu'un mainteneur de TREK a analysé ce plugin à la recherche de logiciels malveillants à chaque version — non pour sa qualité ni son bon fonctionnement. Ce n'est pas une garantie qu'un plugin est inoffensif.",
'admin.plugins.security.title': 'Comment les plugins sont confinés — et les limites',
'admin.plugins.security.isolationTitle': "Chaque plugin s'exécute en isolation",
'admin.plugins.security.isolationBody':
"Un plugin s'exécute comme son propre processus verrouillé qui ne peut lire que ses propres fichiers. Il ne peut pas lire votre base de données, votre secret de connexion ou votre clé de chiffrement, ne peut pas lancer d'autres programmes et ne peut écrire de fichiers nulle part. Son interface s'exécute dans un cadre de navigateur scellé qui ne peut pas lire votre cookie de session ni toucher à la page TREK qui l'entoure.",
'admin.plugins.security.permsTitle': 'Ce que signifient les permissions',
'admin.plugins.security.permsBody':
"Les permissions affichées avant l'installation constituent une limite stricte que TREK applique pendant l'exécution du plugin — il ne peut littéralement rien faire qui ne figure pas dans la liste. Mais elles vous indiquent ce qu'un plugin peut faire, pas ce qu'il fait réellement. Un plugin autorisé à lire vos voyages et à contacter un serveur peut envoyer vos voyages à ce serveur ; lisez donc les permissions et les hôtes sortants, pas seulement la description.",
'admin.plugins.security.limitsTitle': 'Ce que nous ne pouvons pas promettre',
'admin.plugins.security.limitsBody':
"L'isolation est une frontière logicielle solide, mais pas absolue. Un plugin agit avec exactement les droits que vous approuvez ; dans les limites de ces droits, il peut donc se comporter autrement que ne le laisse penser sa description, et il peut envoyer aux hôtes qu'il a déclarés les données qu'il détient légitimement. TREK ne lit ni ne juge ce que fait réellement le code d'un plugin.",
'admin.plugins.security.worstTitle': 'Le pire scénario',
'admin.plugins.security.worstBody':
"Un plugin malveillant que vous activez peut détourner les données et connexions que vous lui avez accordées — par exemple, divulguer les voyages qu'il est autorisé à lire. Il ne peut pas voler de mots de passe, falsifier une connexion admin, exécuter des commandes sur votre serveur ni atteindre des données que vous n'avez pas accordées. Les dégâts restent limités à ce que vous avez approuvé, et désactiver le plugin l'arrête.",
'admin.plugins.security.reviewedTitle': 'Ce que signifie « Vérifié »',
'admin.plugins.security.reviewedBody':
"Un plugin vérifié a été analysé manuellement à la recherche de logiciels malveillants par un mainteneur de TREK à chaque version — contrôlé pour du code hostile, non pour son bon fonctionnement. Ce n'est pas une promesse que le plugin est inoffensif.",
'admin.plugins.security.signedTitle': 'Ce que signifie « Signé »',
'admin.plugins.security.signedBody':
"La somme de contrôle que TREK vérifie à chaque installation prouve que les fichiers sont exactement ceux dont le registre se porte garant. Une signature prouve autre chose : qu'ils proviennent de l'auteur, signés avec une clé que lui seul détient. Un plugin signé possède les deux. Un plugin non signé n'est pas dangereux — il porte simplement une garantie de moins, et la plupart des plugins du registre ne sont pas signés aujourd'hui.",
'admin.plugins.signed': 'Signé',
'admin.plugins.signedHint': "Vérifié avec la clé de signature de l'auteur au moment de l'installation",
'admin.plugins.unsigned': 'Non signé',
'admin.plugins.unsignedHint':
"Les fichiers correspondent à ce dont le registre se porte garant, mais rien ne les relie à l'auteur. Une garantie de moins — pas un danger.",
'admin.plugins.updateBlocked': 'Mise à jour bloquée — {reason}',
'admin.plugins.reviewBlock': 'Examiner',
'admin.plugins.retrusted': 'Nouvelle clé de signature approuvée — le plugin est mis à jour',
'admin.plugins.sig.title': "La signature de {name} n'a pas pu être vérifiée",
'admin.plugins.sig.keyChangedBody':
"La clé de signature de l'auteur n'est pas celle avec laquelle ce plugin a été installé. Les auteurs changent bel et bien de clé — mais un attaquant ayant pris le contrôle du plugin ressemblerait exactement à ceci.",
'admin.plugins.sig.invalidBody':
"Les fichiers ne correspondent pas à la signature de l'auteur. Ce n'est pas ce que l'auteur a signé — soit ils ont été corrompus, soit ils ont été altérés. Impossible de passer outre.",
'admin.plugins.sig.missingBody':
"Ce plugin était signé lorsque vous l'avez installé, mais la nouvelle version ne fournit aucune signature. TREK n'acceptera pas ce recul en silence. Impossible de passer outre.",
'admin.plugins.sig.incompleteBody':
"L'entrée du registre est à moitié signée : elle déclare une clé d'auteur mais la version ne porte aucune signature (ou l'inverse). C'est une erreur du côté du plugin. Impossible de passer outre.",
'admin.plugins.sig.pinnedKey': "Clé utilisée lors de l'installation",
'admin.plugins.sig.newKey': "Clé proposée aujourd'hui",
'admin.plugins.sig.confirmOutOfBand':
"TREK ne peut pas distinguer un changement de clé légitime d'une prise de contrôle — vus d'ici, les deux sont identiques. Confirmez la nouvelle clé auprès de l'auteur via un canal auquel vous faites déjà confiance avant de l'accepter. Une fois que c'est fait, le plugin est mis à jour et la nouvelle clé est mémorisée.",
'admin.plugins.sig.retrustConfirm': 'Faire confiance à la nouvelle clé et mettre à jour',
'admin.plugins.sig.cancel': 'Ne pas lui faire confiance',
'admin.plugins.sig.consentUnsigned':
"Rien ne relie cette version à son auteur — les fichiers correspondent au registre, mais ne portent aucune signature d'auteur.",
'admin.plugins.security.trustTitle': 'En résumé',
'admin.plugins.security.trustBody':
"Installer un plugin revient à installer n'importe quelle application tierce : n'ajoutez que du code provenant d'auteurs de confiance et, en cas de doute, inspectez-le vous-même au préalable. TREK décline toute responsabilité concernant les plugins tiers.",
'admin.plugins.runtimeOn': 'Exécution activée',
'admin.plugins.tabDiscover': 'Découvrir',
'admin.plugins.searchPlaceholder': 'Rechercher des plugins…',
'admin.plugins.filterType': 'Type',
'admin.plugins.filterStatus': 'Statut',
'admin.plugins.sortBy': 'Trier',
'admin.plugins.allTypes': 'Tous les types',
'admin.plugins.allStatuses': 'Tous',
'admin.plugins.stateOff': 'Désactivé',
'admin.plugins.filterUpdate': 'Mise à jour disponible',
'admin.plugins.sortName': 'Nom',
'admin.plugins.sortRecent': 'Récemment mis à jour',
'admin.plugins.sortUpdates': 'Mises à jour en premier',
'admin.plugins.sortDownloads': 'Plus téléchargés',
'admin.plugins.updatesAvailable': '{count} mises à jour disponibles pour vos plugins.',
'admin.plugins.updateAll': 'Tout mettre à jour',
'admin.plugins.noMatchInstalled': 'Aucun plugin installé ne correspond à votre recherche.',
'admin.plugins.noMatchRegistry': 'Aucun plugin du registre ne correspond à votre recherche.',
'admin.plugins.restart': 'Redémarrer',
'admin.plugins.restarted': 'Plugin redémarré',
'admin.plugins.cap.readsTrips': 'Lit vos voyages',
'admin.plugins.cap.readsUsers': 'Lit les profils de base',
'admin.plugins.cap.readsCosts': 'Lit vos dépenses',
'admin.plugins.cap.readsPacking': 'Lit les listes de bagages',
'admin.plugins.cap.readsFiles': 'Lit les fichiers du voyage',
'admin.plugins.cap.writesCosts': 'Ajoute des dépenses',
'admin.plugins.cap.writesPlaces': 'Modifie les lieux',
'admin.plugins.cap.writesDays': 'Modifie les jours',
'admin.plugins.cap.writesItinerary': "Modifie l'itinéraire",
'admin.plugins.cap.writesTrips': 'Modifie les voyages',
'admin.plugins.cap.metadata': 'Ajoute des métadonnées',
'admin.plugins.cap.widget': 'Widget de tableau de bord',
'admin.plugins.cap.heroWidget': "Widget carte d'embarquement",
'admin.plugins.cap.placeSlot': 'Détail du lieu',
'admin.plugins.cap.daySlot': 'Détail du jour',
'admin.plugins.cap.reservationSlot': 'Détail de la réservation',
'admin.plugins.cap.replacesTabs': 'Remplace des onglets du planificateur',
'admin.plugins.cap.realtime': 'Mises à jour en temps réel',
'admin.plugins.cap.notificationChannel': 'Canal de notification',
'admin.plugins.cap.photos': 'Fournit des photos',
'admin.plugins.cap.calendar': 'Fournit des événements de calendrier',
'admin.plugins.cap.placeDetails': 'Enrichit les lieux',
'admin.plugins.cap.warnings': 'Signale les problèmes',
'admin.plugins.cap.events': 'Réagit à l’activité',
'admin.plugins.cap.requiresAddon': 'Nécessite {addon}',
'admin.plugins.cap.dependsOn': 'Requiert {id} {version}',
'admin.plugins.dep.addonDisabledToast': 'Activez d’abord le ou les modules requis : {addons}',
'admin.plugins.dep.autoEnabled': 'Plugin(s) requis activé(s) au préalable : {plugins}',
'admin.plugins.dep.downloaded': '{id} téléchargé',
'admin.plugins.dep.resolveTitle': 'Dépendances manquantes',
'admin.plugins.dep.resolveBody': '« {name} » nécessite l’installation de ces plugins avant de pouvoir être activé.',
'admin.plugins.dep.requires': 'Nécessite {version}',
'admin.plugins.dep.mismatch': 'Nécessite {wanted} — {installed} est installé',
'admin.plugins.dep.download': 'Télécharger',
'admin.plugins.dep.update': 'Mettre à jour',
'admin.plugins.dep.resolveHint': 'Télécharge la dernière version compatible, y compris ses propres dépendances.',
'admin.plugins.dep.trekIncompatible': 'Nécessite TREK {range} — ce serveur exécute {host}',
'admin.plugins.dep.trekUnknown': 'N’indique pas quelles versions de TREK sont prises en charge',
'admin.plugins.installCompatible': 'Installer {version}',
'admin.plugins.incompatible': 'Incompatible',
'admin.plugins.accessTitle': 'Ce à quoi il peut accéder',
'admin.plugins.connectsTitle': 'Se connecte à',
'admin.plugins.detailsTitle': 'Détails',
'admin.plugins.noAccess': 'Ne nécessite aucun accès particulier.',
'admin.plugins.metaVersion': 'Version',
'admin.plugins.metaSize': 'Taille',
'admin.plugins.metaRequires': 'Nécessite',
'admin.plugins.metaReviewed': 'Vérifié le',
'admin.plugins.downloads': 'Téléchargements',
'admin.addons.title': 'Extensions',
'admin.addons.subtitle': 'Activez ou désactivez des fonctionnalités pour personnaliser votre expérience TREK.',
'admin.addons.catalog.memories.name': 'Photos (Immich)',
'admin.addons.catalog.memories.description': 'Partagez vos photos de voyage via votre instance Immich',
'admin.addons.catalog.mcp.name': 'MCP',
'admin.addons.catalog.mcp.description': "Protocole de contexte de modèle pour l'intégration d'assistants IA",
'admin.addons.catalog.packing.name': 'Listes',
'admin.addons.catalog.packing.description': 'Listes de bagages et tâches à faire pour vos voyages',
'admin.addons.catalog.budget.name': 'Coûts',
'admin.addons.catalog.budget.description': 'Suivez les dépenses et planifiez votre budget de voyage',
'admin.addons.catalog.documents.name': 'Documents',
'admin.addons.catalog.documents.description': 'Stockez et gérez vos documents de voyage',
'admin.addons.catalog.vacay.name': 'Vacances',
'admin.addons.catalog.vacay.description': 'Planificateur de vacances personnel avec vue calendrier',
'admin.addons.catalog.atlas.name': 'Atlas',
'admin.addons.catalog.atlas.description': 'Carte du monde avec pays visités et statistiques de voyage',
'admin.addons.catalog.collab.name': 'Collaboration',
'admin.addons.catalog.collab.description': 'Notes en temps réel, sondages et chat pour la planification de voyage',
'admin.addons.subtitleBefore': 'Activez ou désactivez des fonctionnalités pour personnaliser votre expérience ',
'admin.addons.subtitleAfter': '.',
'admin.addons.enabled': 'Activé',
'admin.addons.disabled': 'Désactivé',
'admin.addons.type.trip': 'Voyage',
'admin.addons.type.global': 'Global',
'admin.addons.type.integration': 'Intégration',
'admin.addons.tripHint': 'Disponible comme onglet dans chaque voyage',
'admin.addons.globalHint': 'Disponible comme section autonome dans la navigation principale',
'admin.addons.integrationHint': 'Services backend et intégrations API sans page dédiée',
'admin.addons.toast.updated': 'Extension mise à jour',
'admin.addons.toast.error': "Échec de la mise à jour de l'extension",
'admin.addons.noAddons': 'Aucune extension disponible',
'admin.weather.title': 'Données météo',
'admin.weather.badge': 'Depuis le 24 mars 2026',
'admin.weather.description':
'TREK utilise Open-Meteo comme source de données météo. Open-Meteo est un service météo gratuit et open source — aucune clé API requise.',
'admin.weather.forecast': 'Prévisions sur 16 jours',
'admin.weather.forecastDesc': 'Auparavant 5 jours (OpenWeatherMap)',
'admin.weather.climate': 'Données climatiques historiques',
'admin.weather.climateDesc': 'Moyennes des 85 dernières années pour les jours au-delà des prévisions de 16 jours',
'admin.weather.requests': '10 000 requêtes / jour',
'admin.weather.requestsDesc': 'Gratuit, aucune clé API requise',
'admin.weather.locationHint':
"La météo est basée sur le premier lieu avec des coordonnées de chaque jour. Si aucun lieu n'est attribué à un jour, un lieu de la liste est utilisé comme référence.",
'admin.tabs.audit': 'Audit',
'admin.audit.subtitle':
"Événements sensibles de sécurité et d'administration (sauvegardes, utilisateurs, 2FA, paramètres).",
'admin.audit.empty': "Aucune entrée d'audit.",
'admin.audit.refresh': 'Actualiser',
'admin.audit.loadMore': 'Charger plus',
'admin.audit.showing': '{count} chargées · {total} au total',
'admin.audit.col.time': 'Heure',
'admin.audit.col.user': 'Utilisateur',
'admin.audit.col.action': 'Action',
'admin.audit.col.resource': 'Ressource',
'admin.audit.col.ip': 'IP',
'admin.audit.col.details': 'Détails',
'admin.tabs.mcpTokens': 'Accès MCP',
'admin.mcpTokens.title': 'Accès MCP',
'admin.mcpTokens.subtitle': 'Gérer les sessions OAuth et les tokens API de tous les utilisateurs',
'admin.mcpTokens.sectionTitle': 'Tokens API',
'admin.mcpTokens.owner': 'Propriétaire',
'admin.mcpTokens.tokenName': 'Nom du token',
'admin.mcpTokens.created': 'Créé',
'admin.mcpTokens.lastUsed': 'Dernière utilisation',
'admin.mcpTokens.never': 'Jamais',
'admin.mcpTokens.empty': "Aucun token MCP n'a encore été créé",
'admin.mcpTokens.deleteTitle': 'Supprimer le token',
'admin.mcpTokens.deleteMessage':
"Ce token sera révoqué immédiatement. L'utilisateur perdra l'accès MCP via ce token.",
'admin.mcpTokens.deleteSuccess': 'Token supprimé',
'admin.mcpTokens.deleteError': 'Impossible de supprimer le token',
'admin.mcpTokens.loadError': 'Impossible de charger les tokens',
'admin.oauthSessions.sectionTitle': 'Sessions OAuth',
'admin.oauthSessions.clientName': 'Client',
'admin.oauthSessions.owner': 'Propriétaire',
'admin.oauthSessions.scopes': 'Portées',
'admin.oauthSessions.created': 'Créé',
'admin.oauthSessions.empty': 'Aucune session OAuth active',
'admin.oauthSessions.revokeTitle': 'Révoquer la session',
'admin.oauthSessions.revokeMessage': "Cette session OAuth sera révoquée immédiatement. Le client perdra l'accès MCP.",
'admin.oauthSessions.revokeSuccess': 'Session révoquée',
'admin.oauthSessions.revokeError': 'Impossible de révoquer la session',
'admin.oauthSessions.loadError': 'Impossible de charger les sessions OAuth',
'admin.tabs.github': 'GitHub',
'admin.github.title': 'Historique des versions',
'admin.github.subtitle': 'Dernières mises à jour de {repo}',
'admin.github.latest': 'Dernière',
'admin.github.prerelease': 'Pré-version',
'admin.github.showDetails': 'Afficher les détails',
'admin.github.hideDetails': 'Masquer les détails',
'admin.github.loadMore': 'Charger plus',
'admin.github.loading': 'Chargement…',
'admin.github.support': 'Aidez à poursuivre le développement de TREK',
'admin.github.error': 'Impossible de charger les versions',
'admin.github.by': 'par',
'admin.update.available': 'Mise à jour disponible',
'admin.update.text': 'TREK {version} est disponible. Vous utilisez {current}.',
'admin.update.button': 'Voir sur GitHub',
'admin.update.install': 'Installer la mise à jour',
'admin.update.confirmTitle': 'Installer la mise à jour ?',
'admin.update.confirmText':
'TREK sera mis à jour de {current} vers {version}. Le serveur redémarrera automatiquement ensuite.',
'admin.update.dataInfo':
'Toutes vos données (voyages, utilisateurs, clés API, importations, Vacances, Atlas, budgets) seront préservées.',
'admin.update.warning': "L'application sera brièvement indisponible pendant le redémarrage.",
'admin.update.confirm': 'Mettre à jour maintenant',
'admin.update.installing': 'Mise à jour…',
'admin.update.success': 'Mise à jour installée ! Le serveur redémarre…',
'admin.update.failed': 'Échec de la mise à jour',
'admin.update.backupHint': 'Nous recommandons de créer une sauvegarde avant la mise à jour.',
'admin.update.backupLink': 'Aller aux sauvegardes',
'admin.update.howTo': 'Comment mettre à jour',
'admin.update.dockerText':
'Votre instance TREK fonctionne dans Docker. Pour mettre à jour vers {version}, exécutez les commandes suivantes sur votre serveur :',
'admin.update.nonDockerText':
"Cette instance TREK ne fonctionne pas dans Docker. Pour mettre à jour vers {version}, relancez la méthode d'installation ou de mise à jour que vous avez utilisée — par exemple, sur Proxmox Community Scripts, lancez la mise à jour depuis la console LXC :",
'admin.update.wikiLink': 'Ouvrir le guide de mise à jour',
'admin.update.reloadHint': 'Veuillez recharger la page dans quelques secondes.',
'admin.tabs.permissions': 'Permissions',
'admin.notifications.emailPanel.title': 'Email (SMTP)',
'admin.notifications.webhookPanel.title': 'Webhook',
'admin.notifications.inappPanel.title': 'In-App',
'admin.notifications.inappPanel.hint':
'Les notifications in-app sont toujours actives et ne peuvent pas être désactivées globalement.',
'admin.notifications.adminWebhookPanel.title': 'Webhook admin',
'admin.notifications.adminWebhookPanel.hint':
"Ce webhook est utilisé exclusivement pour les notifications admin (ex. alertes de version). Il est séparé des webhooks utilisateur et s'active automatiquement si une URL est configurée.",
'admin.notifications.adminWebhookPanel.saved': 'URL du webhook admin enregistrée',
'admin.notifications.adminWebhookPanel.testSuccess': 'Webhook de test envoyé avec succès',
'admin.notifications.adminWebhookPanel.testFailed': 'Échec du webhook de test',
'admin.notifications.adminWebhookPanel.alwaysOnHint':
"Le webhook admin s'active automatiquement si une URL est configurée",
'admin.notifications.ntfy': 'Ntfy',
'admin.ntfy.hint':
'Permet aux utilisateurs de configurer leurs propres sujets ntfy pour les notifications push. Définissez le serveur par défaut ci-dessous pour pré-remplir les paramètres utilisateur.',
'admin.notifications.testNtfy': 'Envoyer un Ntfy de test',
'admin.notifications.testNtfySuccess': 'Ntfy de test envoyé avec succès',
'admin.notifications.testNtfyFailed': "Échec de l'envoi du Ntfy de test",
'admin.notifications.adminNtfyPanel.title': 'Ntfy admin',
'admin.notifications.adminNtfyPanel.hint':
"Ce sujet Ntfy est utilisé exclusivement pour les notifications admin (ex. alertes de version). Il est séparé des sujets par utilisateur et s'active toujours lorsqu'il est configuré.",
'admin.notifications.adminNtfyPanel.serverLabel': 'URL du serveur Ntfy',
'admin.notifications.adminNtfyPanel.serverHint':
'Utilisé également comme serveur par défaut pour les notifications ntfy des utilisateurs. Laisser vide pour utiliser ntfy.sh. Les utilisateurs peuvent le modifier dans leurs propres paramètres.',
'admin.notifications.adminNtfyPanel.serverPlaceholder': 'https://ntfy.sh',
'admin.notifications.adminNtfyPanel.topicLabel': 'Sujet admin',
'admin.notifications.adminNtfyPanel.topicPlaceholder': 'trek-admin-alerts',
'admin.notifications.adminNtfyPanel.tokenLabel': "Jeton d'accès (optionnel)",
'admin.notifications.adminNtfyPanel.tokenCleared': "Jeton d'accès admin effacé",
'admin.notifications.adminNtfyPanel.saved': 'Paramètres Ntfy admin enregistrés',
'admin.notifications.adminNtfyPanel.test': 'Envoyer un Ntfy de test',
'admin.notifications.adminNtfyPanel.testSuccess': 'Ntfy de test envoyé avec succès',
'admin.notifications.adminNtfyPanel.testFailed': "Échec de l'envoi du Ntfy de test",
'admin.notifications.adminNtfyPanel.alwaysOnHint': "Le Ntfy admin s'active toujours lorsqu'un sujet est configuré",
'admin.notifications.adminNotificationsHint':
"Configurez quels canaux envoient les notifications admin (ex. alertes de version). Le webhook s'active automatiquement si une URL webhook admin est définie.",
'admin.notifications.tripReminders.title': 'Rappels de voyage',
'admin.notifications.tripReminders.hint':
"Envoie une notification de rappel avant le début d'un voyage (nécessite des jours de rappel définis sur le voyage).",
'admin.notifications.tripReminders.enabled': 'Rappels de voyage activés',
'admin.notifications.tripReminders.disabled': 'Rappels de voyage désactivés',
'admin.tabs.notifications': 'Notifications',
'admin.addons.catalog.journey.name': 'Journal de voyage',
'admin.addons.catalog.journey.description': 'Suivi de voyages et journal avec check-ins, photos et récits quotidiens',
'admin.addons.catalog.collections.name': 'Collections',
'admin.addons.catalog.collections.description':
'Bibliothèque personnelle de lieux — enregistrez des lieux de vos voyages dans des listes nommées, copiez-les dans n’importe quel voyage et partagez-les',
'admin.passkey.title': 'Connexion par passkey',
'admin.passkey.cardHint':
'Permettez aux utilisateurs de se connecter avec des passkeys (WebAuthn). Désactivé par défaut.',
'admin.passkey.login': 'Activer la connexion par passkey',
'admin.passkey.loginHint':
"Affiche une option « Se connecter avec une passkey » et permet aux utilisateurs d'enregistrer des passkeys dans leurs paramètres.",
'admin.passkey.notConfigured':
'Aucun domaine WebAuthn ne peut encore être résolu pour ce déploiement. Définissez APP_URL ou le Relying Party ID ci-dessous — les passkeys restent masquées jusque-là.',
'admin.passkey.rpId': 'Relying Party ID (domaine)',
'admin.passkey.rpIdHint':
"Le domaine nu auquel les passkeys sont liées, ex. trek.example.org. Laissez vide pour le déduire d'APP_URL. Le modifier ultérieurement invalide les passkeys existantes.",
'admin.passkey.origins': 'Origines autorisées',
'admin.passkey.originsHint':
'Origines complètes séparées par des virgules, ex. https://trek.example.org. Laissez vide pour utiliser APP_URL.',
'admin.passkey.reset': 'Réinitialiser les passkeys',
'admin.passkey.resetHint':
"Supprime toutes les passkeys de cet utilisateur (ex. en cas d'appareil perdu). Il pourra toujours se connecter avec son mot de passe.",
'admin.passkey.resetConfirm': 'Supprimer toutes les passkeys de {name} ?',
'admin.passkey.resetDone': '{count} passkey(s) supprimée(s)',
'admin.defaultSettings.mapProvider': 'Moteur cartographique',
'admin.defaultSettings.mapProviderHint':
'La carte par défaut pour tous les utilisateurs de cette instance. Chaque utilisateur peut toujours la remplacer dans ses propres paramètres.',
'admin.defaultSettings.providerLeaflet': 'Standard (gratuit)',
'admin.defaultSettings.providerMapbox': 'Mapbox (3D)',
'admin.defaultSettings.providerMapLibre': 'MapLibre (OpenFreeMap)',
'admin.defaultSettings.mapboxToken': 'Jeton Mapbox partagé',
'admin.defaultSettings.mapboxTokenHint':
"Utilisé pour chaque utilisateur n'ayant pas saisi son propre jeton — ainsi toute l'instance bénéficie de Mapbox sans partager la clé individuellement. Stocké de façon chiffrée.",
'admin.defaultSettings.mapboxStyle': 'Style de carte',
'admin.defaultSettings.mapboxStylePlaceholder': 'Choisissez un style…',
'admin.defaultSettings.mapbox3d': 'Bâtiments & terrain en 3D',
'admin.defaultSettings.mapboxQuality': 'Mode haute qualité',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
'admin.invite.tripLabel': 'Ajouter à un voyage (facultatif)',
'admin.invite.tripNone': 'Aucun voyage',
'admin.invite.tripHint':
"Le nouvel utilisateur est automatiquement ajouté à ce voyage lorsqu'il s'inscrit via le lien.",
'admin.invite.boundTo': 'ajoute à {trip}',
};
export default admin;